What are the rules for template instantiation when we pass a (multi)derived class to a template function expecting base class? For example:
#include <iostream>
template <int x>
struct C {};
struct D : C<0>, C<1> {};
template <int x>
void f (const C<x> &y) { std::cout << x << "\n"; }
int main ()
{
f (D ());
}
MSVC 2015 prints 0, clang 3.8 - 1 and gcc 6.2 gives compiler error (Demo). And even if you SFINAE-away all overloads except one, the result will still be different:
#include <iostream>
template <int x> struct C {};
template<>
struct C<0> { using type = void; };
struct D : C<0>, C<1> {};
template <int x, typename = typename C<x>::type>
void f (const C<x> &y) { std::cout << x << "\n"; }
int main ()
{
f (D ());
}
Now it compiles only with MSVC, and if you swap C<0> and C<1> only clang will compile it. The problem is that MSVC only tries to instantiate first base, clang - last and gcc prints error too early. Which compiler is right?
gcc 5.4:
/tmp/gcc-explorer-compiler11685-58-1h67lnf/example.cpp: In function 'int main()':
13 : error: no matching function for call to 'f(D)'
f (D ());
^
9 : note: candidate: template<int x> void f(const C<x>&)
void f (const C<x> &y) { std::cout << x << "\n"; }
^
9 : note: template argument deduction/substitution failed:
13 : note: 'const C<x>' is an ambiguous base class of 'D'
f (D ());
^
Compilation failed
Which seems to me to be the correct result, since C<0> and C<1> are equally specialised.
Same result for gcc 6.2
clang 3.8.1 compiles it, which in my view is a compiler bug.
update:
I don't know the actual use case but I was wonder whether this might work for you:
#include <utility>
#include <iostream>
template<class T>
struct has_type
{
template<class U> static auto test(U*) -> decltype(typename U::type{}, std::true_type());
static auto test(...) -> decltype(std::false_type());
using type = decltype(test((T*)0));
static const auto value = type::value;
};
template <int x> struct C {};
template<>
struct C<0> { using type = int; };
template<int...xs>
struct enumerates_C : C<xs>...
{
};
struct D : enumerates_C<0, 1> {};
template<int x, std::enable_if_t<has_type<C<x>>::value>* = nullptr>
void f_impl(const C<x>& y)
{
std::cout << x << "\n";
}
template<int x, std::enable_if_t<not has_type<C<x>>::value>* = nullptr>
void f_impl(const C<x>& y)
{
// do nothing
}
template <int...xs>
void f (const enumerates_C<xs...> &y)
{
using expand = int[];
void(expand { 0,
(f_impl(static_cast<C<xs> const &>(y)),0)...
});
}
int main ()
{
f (D ());
}
expected output (tested on apple clang):
0
Related
Why does this fail to compile? I am trying to reduce the amount of repetition since in the real code the class name is huge and the type is actually a very long name
https://godbolt.org/z/8YarWs
#include <vector>
template<typename T>
struct S {
using type = std::vector<T>;
type f() const;
};
template<typename T>
using type_t = typename S<T>::type;
template <typename T>
type_t<T> S<T>::f() const { }
The code above fails as below
<source>:17:14: error: no declaration matches 'type_t<T> S<T>::f() const'
17 | type_t < T > S < T >::f() const
| ^~~~~~~
<source>:8:10: note: candidate is: 'S<T>::type S<T>::f() const'
8 | type f() const
| ^
<source>:4:8: note: 'struct S<T>' defined here
4 | struct S
| ^
This is a GCC bug; the closest(1) confirmed open bug report seems to be:
Bug 69348: alias declarations can not be used inside qualifiers of declarators
which highlights that GCC rejects the following well-formed program:
template <class T>
struct X {
int foo();
};
template <class T>
using foo2 = X<T>;
template <class T>
int foo2<T>::foo()
{
}
with the error message
error: invalid use of incomplete type 'struct X<T>'
(1) Scanning GCC:s bugzilla, there seems to be numerous "rejects valid" (open/unconfirmed) bug reports relating to alias templates, where in most reports clang is reported to accept the (arguably well-formed) example programs.
I don't remember the exact reason why, but I know it has something to do with the sequence in which the compiler resolves the type names and symbols...
You could get it to work by changing a bit the structure of the declaration.
Since I don't know how much of this is possible for you, I included some variants.
https://godbolt.org/z/bGPGhW
#include <vector>
#include <iostream>
template <typename T>
struct S
{
using type = std::vector<T>;
auto f() const -> decltype(type());
auto g() const -> decltype(type());
decltype(type()) h() const;
};
template <typename T>
using type_t = typename S<T>::type;
template <typename T>
auto S<T>::f() const -> decltype(type())
{
std::cout << "foo" << std::endl;
type_t<T> t;
return t;
}
template <typename T>
using other_type_t = decltype(typename S<T>::type());
template <typename T>
auto S<T>::g() const -> other_type_t<T>
{
std::cout << "bar" << std::endl;
other_type_t<T> t;
return t;
}
template <typename T>
using last_type_t = decltype(typename S<T>::type());
template <typename T>
last_type_t<T> S<T>::h() const
{
std::cout << "foobar" << std::endl;
last_type_t<T> t;
return t;
}
int main(){
S<int> s;
auto t = s.f();
auto r = s.g();
auto l = s.h();
return 0;
}
Output:
Program returned: 0
Program stdout
foo
bar
foobar
The following example:
#include <iostream>
template<typename T>
struct X {
static constexpr bool B = std::is_same_v<T, int>;
template <bool Z = B, std::enable_if_t<Z, int> = 0>
X(T t) { std::cout << t << " - int\n"; }
template <bool Z = B, std::enable_if_t<!Z, int> = 0>
X(T t) { std::cout << t << " - not int\n"; }
};
int main()
{
X x(5);
X y(5.0);
}
Results in an ambiguous deduction error when compiled using clang++ (I tried up to 11.0) with -std=c++1z. Compilation terminates successfully with g++ 9.3.0.
Who is right? Why? Is there a bug associated with this?
Why does callB(f) compile and callA(f) does not compile? Aren't they kind of the same?
I have tried with gcc and MSVC... same results.
#include <functional>
template<typename T>
struct Function
{
typedef std::function<void(T)> type;
};
template<typename T = int>
void callA(std::function<void(T)> f)
{
f(T());
}
template<typename T = int>
void callB(typename Function<T>::type f)
{
f(T());
}
int main()
{
auto f = [](int) { };
callA(f); // compile error: "template argument deduction/substitution failed: main()::<lambda(int)>’ is not derived from ‘std::function<void(T)>"
callB(f); // compiles
return 0;
}
Triggered by a comment to this answer I would like to write (in C++11) a
template <typename T>
struct has_out_op { static const bool value = ???; }
to dis/enable a member function depending on std::cout << t; being valid for some T t. I came this far...
#include <iostream>
struct can_convert_to_base{}; // but does not when there is a better match
struct base {base(can_convert_to_base);};
template <typename T>
auto test(const T& t,can_convert_to_base)
-> decltype( std::cout << t);
template <typename T>
std::false_type test(const T& t,base);
template <typename T>
struct has_out_op {
static const bool value =
!std::is_same<std::false_type,
decltype( test(T(),can_convert_to_base()) )
>::value;
};
struct A{};
int main() {
std::cout << has_out_op<int>::value; // prints 1
std::cout << has_out_op<A>::value; // prints 0
}
This seems to work, but when I use it for what I was actually aiming for:
struct B {
template <typename T>
typename std::enable_if<has_out_op<T>::value,B&>::type operator<<(const T& t) {
std::cout << t;
return *this;
}
};
int main() {
B b;
b << "1";
}
I get the error
prog.cc: In instantiation of 'const bool has_out_op<char [2]>::value':
prog.cc:25:60: required by substitution of 'template<class T> typename std::enable_if<has_out_op<T>::value, B&>::type B::operator<<(const T&) [with T = char [2]]'
prog.cc:31:14: required from here
prog.cc:17:67: error: functional cast to array type 'char [2]'
decltype( test(T(),can_convert_to_base()) )
^
prog.cc: In function 'int main()':
prog.cc:31:11: error: no match for 'operator<<' (operand types are 'B' and 'const char [2]')
b << "1";
^
Then I realized that my has_out_op requires T to be default constructible, and since that I am turning in circles. When I have a value I can easily test if std::cout << t; is valid, but with the type alone I have no idea how to properly implement has_out_op.
How to detect if there is a matching overload for std::cout << t; given only decltype(t)?
Note that I already know how to dis/enable B::operator<< properly, but out of courisity I am still struggling with getting has_out_op right.
std::declval<T>() to the rescue:
Converts any type T to a reference type, making it possible to use
member functions in decltype expressions without the need to go
through constructors.
Note that because no definition exists for declval, it can only be
used in unevaluated contexts; i
...
decltype( test(std::declval<T>(),can_convert_to_base()) )
...
Since we're already here, your solution is overly complicated. This is how I would do it:
struct B {
template <typename T, class = decltype(std::cout << std::declval<T>())>
B& operator<<(const T& t)
{
std::cout << t;
return *this;
}
};
though I would be interested if there is a simpler solution for
has_out_op
template <typename T>
struct has_out_op_impl
{
template <class U, class = decltype(std::cout << std::declval<U>())>
static auto foo(U) -> std::true_type;
static auto foo(...) -> std::false_type;
using Type = decltype(foo(std::declval<T>()));
};
template <class T>
struct has_out_op : has_out_op_impl<T>::Type
{};
struct A{};
int t1()
{
static_assert(has_out_op<int>::value == true, "");
static_assert(has_out_op<A>::value == false, "");
}
We have:
template<typename T>
struct A {
void foo(int a) {
T::foo(a);
}
};
template<typename T>
struct B {
template struct A<T>; // concept check
};
So, I define a concept checker A that checks T by forwarding foo to T::foo.
Now, I want to check whether the argument passed to B satisfies the concept A by explicit instantiation, but the compiler complains that it's the wrong namespace. How can I fix that?
Something like this perhaps:
template<typename T, void(T::*)(int)>
struct A {};
template<typename T>
struct B {
using Check = A<T, &T::foo>;
};
Demo
Or this:
template<typename T>
struct B {
static_assert(
std::is_same<decltype(&T::foo), void(T::*)(int)>::value,
"No T::foo(int) member");
};
So, I found a working example:
#include <tuple>
template<typename A>
struct IA : A {
void foo(int a) {
A::foo(a);
}
void bar(double a) {
A::bar(a);
}
static constexpr auto $ = std::make_tuple(&IA::foo, &IA::bar);
};
template<typename T>
struct B {
// trigger concept/interface check of T "implements" IA
static constexpr auto $ = IA<T>::$;
};
struct Test {
void foo(int a) {}
void bar(int a, int b) {}
};
int main() {
B<Test> b;
b = b;
}
The generation of $ in the structs triggers the compilation. The compiler in the example above correctly complains with:
In instantiation of 'void IA<A>::bar(double) [with A = Test]':
13:57: required from 'constexpr const std::tuple<void (IA<Test>::*)(int), void (IA<Test>::*)(double)> IA<Test>::$'
18:27: recursively required from 'constexpr const std::tuple<void (IA<Test>::*)(int), void (IA<Test>::*)(double)> B<Test>::$'
18:27: required from 'struct B<Test>'
28:13: required from here
10:17: error: no matching function for call to 'IA<Test>::bar(double&)'
10:17: note: candidate is:
24:10: note: void Test::bar(int, int)
24:10: note: candidate expects 2 arguments, 1 provided