2

How do I make the last line in main() compile?

#include <initializer_list>
#include <type_traits>
#include <functional>

template <typename T>
struct foo {
    foo(std::initializer_list<T>) { }

    template <typename C> struct is_foo : std::false_type { };
    template <typename U> struct is_foo<foo<U>> : std::true_type { };

    template <typename Compare>
    std::enable_if_t<!is_foo<Compare>::value> bar(foo&, Compare comp) {
        bool b = comp(T(), T());  // This line compiles thanks to is_foo<Compare>.
    }

    void bar(foo&& f) { bar(std::forward<foo>(f), std::less<T>()); }

    template <typename... Foos>
    void bar(Foos&&...) { }
};

int main() {
    foo<int> f = {1,2,3};
    f.bar({4,5,6});  // Compiles fine
    f.bar(f,f);  // Compiles fine (thanks to is_foo<Compare>)
    f.bar(f,{4,5,6});  // Won't compile
}

It is supposed to call up foo<T>::bar(Foos&&...).

Test.cpp:27:17: error: no matching function for call to 'foo<int>::bar(foo<int>&, <brace-enclosed initializer list>)'
  f.bar(f,{4,5,6});  // Won't compile
                 ^
Test.cpp:13:44: note: candidate: template<class Compare> std::enable_if_t<(! foo<T>::is_foo<C>::value)> foo<T>::bar(foo<T>&, Compare) [with Compare = Compare; T = int]
  std::enable_if_t<!is_foo<Compare>::value> bar(foo&, Compare comp) {
                                            ^~~
Test.cpp:13:44: note:   template argument deduction/substitution failed:
Test.cpp:27:17: note:   couldn't deduce template parameter 'Compare'
  f.bar(f,{4,5,6});
prestokeys
  • 4,817
  • 3
  • 20
  • 43
  • Unfortunately, a braced initializer list cannot match a forwarding reference. A braced initializer list "needs to know" what it is initializing. And a forwarding reference also needs to know what it is forwarding. Neither one knows what the heck its supposed to be. – Sam Varshavchik Dec 16 '17 at 02:28
  • the easiest way would be to use a simple foo cast – code1x1.de Dec 16 '17 at 02:52

2 Answers2

2

Not sure to understand what do you exactly want but, if your intend is obtain a variadic bar() function that receive zero or more foo<T> arguments (or arguments that can be used to initialize foo<T>), well... if you accept a limit for the number of arguments (say 63, in following example) there is a trick that W.F. showed one time that I suppose can be adapted for your case.

If you define a typer template alias

template <typename T, std::size_t>
using typer = T;

and a recursive struct proBar

template <typename T, std::size_t N = 64U,
          typename = std::make_index_sequence<N>>
struct proBar;

template <typename T, std::size_t N, std::size_t... Is>
struct proBar<T, N, std::index_sequence<Is...>> : public proBar<T, N-1U>
 {
   using proBar<T, N-1U>::bar;

   void bar (typer<T, Is>... ts)
    { }
 };

template <typename T>
struct proBar<T, 0U, std::index_sequence<>>
 {
    void bar ()
     { }
 };

that permit to define the bar() function you need, your template struct foo become

template <typename T>
struct foo : public proBar<foo<T>>
 {
   using proBar<foo<T>>::bar;

   foo (std::initializer_list<T>)
    { }

   template <typename Compare>
   auto bar (foo &, Compare comp) -> decltype(comp(T(), T()), void())
    { bool b = comp(T(), T()); }
 };

The following is a full compiling example

#include <initializer_list>

template <typename T, std::size_t>
using typer = T;

template <typename T, std::size_t N = 64U,
          typename = std::make_index_sequence<N>>
struct proBar;

template <typename T, std::size_t N, std::size_t... Is>
struct proBar<T, N, std::index_sequence<Is...>> : public proBar<T, N-1U>
 {
   using proBar<T, N-1U>::bar;

   void bar (typer<T, Is>... ts)
    { }
 };

template <typename T>
struct proBar<T, 0U, std::index_sequence<>>
 {
    void bar ()
     { }
 };

template <typename T>
struct foo : public proBar<foo<T>>
 {
   using proBar<foo<T>>::bar;

   foo (std::initializer_list<T>)
    { }

   template <typename Compare>
   auto bar (foo &, Compare comp) -> decltype(comp(T(), T()), void())
    { bool b = comp(T(), T()); }
 };



int main()
 {
   foo<int> f = {1, 2, 3};
   f.bar({4, 5, 6});
   f.bar(f, f);
   f.bar(f, {4, 5, 6}); // now compile
 }
max66
  • 65,235
  • 10
  • 71
  • 111
  • Thanks for the radical solution. Will CRTP work (and is needed) if members of `foo` (of `this`) are needed in the defining body of `proBar>::bar`? I guess I should just give it try. – prestokeys Dec 16 '17 at 03:43
  • @prestokeys - answer a little improved (I think): I've modified the compare `bar()` version enabling it, via SFINAE, using `auto` and `decltype()` instead of `isFoo` (and removed `isFoo`). – max66 Dec 16 '17 at 03:54
0

This works.

#include <initializer_list>
#include <type_traits>
#include <functional>

template <typename T>
struct foo {
    foo(std::initializer_list<T>) { }

    template <typename C> struct is_foo : std::false_type { };
    template <typename U> struct is_foo<foo<U>> : std::true_type { };

    template <typename Compare>
    std::enable_if_t<!is_foo<Compare>::value> bar(foo&, Compare comp) {
        bool b = comp(T(), T());  // This line compiles thanks to is_foo<Compare>.
    }

    void bar(foo&& f) { bar(std::forward<foo>(f), std::less<T>()); }

    template <typename... Foos>
    void bar(Foos&&...) { }
};

int main() {
    foo<int> f = { 1,2,3 };
    f.bar({ 4,5,6 });  // Compiles fine
    f.bar(f, f);  // Compiles fine (thanks to is_foo<Compare>)
    f.bar(f, foo<int>({ 4,5,6 }));  // Won't compile
}
code1x1.de
  • 304
  • 2
  • 13