I have this function template:
template <class TemplateArgument, template<class> class TemplateType>
TemplateArgument f(const TemplateType<TemplateArgument>& arg)
{
return TemplateArgument();
}
When used like this, it fails to compile:
struct A {};
template <typename T> struct S {};
template <typename T> struct B : public S<T> {};
struct C : public B<A> {};
int main()
{
f(C());
return 0;
}
And the error message is:
<source>: In function 'int main()':
<source>:15:10: error: no matching function for call to 'f(C)'
f(C());
^
<source>:2:18: note: candidate: template<class TemplateArgument, template<class> class TemplateType> TemplateArgument f(const TemplateType<TemplateArgument>&)
TemplateArgument f(const TemplateType<TemplateArgument>& arg)
^
<source>:2:18: note: template argument deduction/substitution failed:
<source>:15:10: note: 'const TemplateType<TemplateArgument>' is an ambiguous base class of 'C'
f(C());
^
Happens with GCC (any version) and clang (any version). Does not happen with MSVC. Live demo: https://godbolt.org/g/eWxeHJ
Why does this error occur? I fail to see any ambiguity, the "ambiguous base class" error usually occurs in multiple inheritance situations, does it not?
How can I make my code compile (deduce template arguments correctly)?
Note that I cannot edit the A, B, C, S classes and their relation to each other, I can only edit my function f() to accept these classes properly.
The compiler is not sure whether to deduce args type as B<A> or S<A>. I'm not sure about this specific case but MSVC is known to violate the standard especially when it comes to templates.
As for your function, you need to resolve this ambiguity yourself by explicitly casting to the appropriate base:
f((const B<A> &)C());
or by specifying template parameters explicitly:
f<A, B>(C());
Generally whenever there is any ambiguity in the language it is never automatically resolved by the compiler because it would just be a speculation about what exactly did user intend, which might be right in some cases and completely wrong in others.
Related
How can I make the following code compile?
I'm trying to check if BigStruct exist in a type, and enable f if it does.
#include <type_traits>
struct A {
using BigStruct = int;
};
struct C {
};
template <typename T>
struct B {
void f(typename T::BigStruct t) requires requires {T::BigStruct;} {}
};
int main() {
B<A> b1;
B<C> b2;
}
Error I got:
<source>:11:24: error: no type named 'BigStruct' in 'C'
void f(typename T::BigStruct t) requires requires {T::BigStruct;} {}
~~~~~~~~~~~~^~~~~~~~~
<source>:16:8: note: in instantiation of template class 'B<C>' requested here
B<C> b2;
^
1 error generated.
ASM generation compiler returned: 1
<source>:11:24: error: no type named 'BigStruct' in 'C'
void f(typename T::BigStruct t) requires requires {T::BigStruct;} {}
~~~~~~~~~~~~^~~~~~~~~
<source>:16:8: note: in instantiation of template class 'B<C>' requested here
B<C> b2;
^
1 error generated.
Execution build compiler returned: 1
Here's a godbolt link for x86-64 clang trunk.
Concept checking for non-template functions happens after the function's signature has been generated. That means the parameter list has to exist. And therefore, it must be syntactically valid.
There's not much you can do in this circumstance, if you don't want to restrict the entire class, besides the old pre-C++20 strategy of making the function itself a template:
template<typename U = T>
requires requires {typename U::BigStruct;}
void f(typename U::BigStruct t) {}
Please, have a look at this small sample code
#include <type_traits>
struct base {
template <typename T>
int func(T);
};
struct derived: base {
using base::func;
template <typename T, std::enable_if_t<std::is_same_v<T,const char*>>* = nullptr>
int func(T);
};
auto x = derived().func(1);
It compiles fine with gcc and icc, it doesn't with clang, which complains like this:
<source>:17:20: error: no matching member function for call to 'func'
auto x = derived().func(1);
~~~~~~~~~~^~~~
<source>:13:9: note: candidate template ignored: requirement 'std::is_same_v<int, const char *>' was not satisfied [with T = int]
int func(T);
^
As if no using-declaration were added to struct derived. If I weren't using templates, I'd for sure know that this would be clang's problem. However, I am not sure the fact I'm using templates and SFINAE implies different rules got to be used, so here's my question: which compiler is right? Clang, or gcc and icc?
Here's a working example of the issue on godbolt: https://godbolt.org/z/xv98SP
I want to use the standard code to write the utils like std::is_union,we know class type can not extends union type,it's error,so some code like these
#include <iostream>
template<typename T>
class class_type_can_extends :public T{
public:
using type = void;
};
template<typename T,typename U = void>
struct is_not_union:std::false_type {
};
template<typename T>
struct is_not_union < T, std::void_t<typename class_type_can_extends <T>::type >> :std::true_type {
};
class c_data{
};
union u_data{
};
int main(){
/*#1*/ std::cout<< is_not_union<c_data>::value<<std::endl; /*print true*/
/*#2*/ std::cout<< is_not_union<u_data>::value<<std::endl; /*this code make
all complier error*/
}
g++ print error:
main.cpp: In instantiation of ‘class class_type_can_extends<u_data>’:
main.cpp:26:43: recursively required by substitution of ‘template<class T> struct is_not_union<T, std::void_t<typename class_type_can_extends<T>::type> > [with T = u_data]’
main.cpp:26:43: required from here
main.cpp:3:7: error: base type ‘u_data’ fails to be a struct or class type
class class_type_can_extends :public T {
clang print error:
main.cpp:3:38: error: unions cannot be base classes
class class_type_can_extends :public T {
~~~~~~~^
main.cpp:14:47: note: in instantiation of template class 'class_type_can_extends<u_data>' requested here
struct is_not_union < T, std::void_t<typename class_type_can_extends <T>::type >> :std::true_type {
^
main.cpp:26:23: note: during template argument deduction for class template partial specialization 'is_not_union<T,
std::void_t<typename class_type_can_extends<T>::type> >' [with T = u_data]
/*#2*/ std::cout << is_not_union<u_data>::value << std::endl; /*this code make
^
main.cpp:26:23: note: in instantiation of template class 'is_not_union<u_data, void>' requested here
1 error generated.
vs:
error C2569
why #2 code make complier error,The complier would be using SFINAE rules on #2 code(substituted T by "u_data" ,then Failed ),and to chose primary template?why the sfinae not effective here,may be a bug here?
From cppreference:
Only the failures in the types and expressions in the immediate context of the function type or its template parameter types or its explicit specifier (since C++20) are SFINAE errors. If the evaluation of a substituted type/expression causes a side-effect such as instantiation of some template specialization, generation of an implicitly-defined member function, etc, errors in those side-effects are treated as hard errors
SFINAE applies on immediate context, here you have a hard error failure.
In typename class_type_can_extends<T>::type, SFINAE applies if type doesn't exist, not if instantiation of class_type_can_extends<T> fails.
Notice that we cannot distinguish between union and class types using only standard C++
(without std::is_union). Most compilers provide intrinsics for that.
I have never gotten a great explanation of how template argument deduction really works, so I'm not sure how to explain behavior I'm seeing in the following:
template<typename T>
struct Base
{
protected:
template<bool aBool = true>
static void Bar(int)
{
}
};
template<typename T>
class Derived : public Base<T>
{
public:
void Foo() { Base<T>::Bar<false>(5); }
};
int main()
{
Derived<int> v;
v.Foo();
return 0;
}
This code won't build, and gives the error:
main.cpp: In instantiation of 'void Derived<T>::Foo() [with T = int]':
main.cpp:25:8: required from here main.cpp:19:15: error: invalid
operands of types '<unresolved overloaded function type>' and 'bool'
to binary 'operator<'
If you change the 2 Base<T>s in Derived to Base<int>, it compiles. If you change the call to Bar() to Base<T>::template Bar<false>(5);, it also compiles.
The one-liner I saw as an explanation for this is that the compiler doesn't know that Bar is a template, presumably because it doesn't know what Base is until a specialization of Derived is declared. But once the compiler starts generating code for Foo(), Base<T> has already been defined, and the type of Bar can be determined. What is causing the compiler to assume the symbol Bar is not a template, and attempting to apply operator<() instead?
I assume it has to do with the rules of when templates are evaluated in the compilation process - I guess what I'm looking for is a good comprehensive explanation of this process, such that the next time I run into code like the below, I can deduce the answer without the help of the good people on stack overflow.
Note I'm compiling with g++ 4.7, with c++x11 support.
void Foo() { Base<T>::Bar<false>(5); }
In this context Base<T> is a dependent name. To access a member template of a dependent name you need to add the template keyword:
void Foo() { Base<T>::template Bar<false>(5); }
Otherwise Base<T>::Bar will be parsed as a non-template member and < as less-than.
As of why the template is required, the reason is two-phase lookup. The error is triggered during the first pass, before the type is substituted, so the compiler does not know what is the definition of Base<T>. Consider for example that you added an specialization of Bar for int that had a non-template Bar member (say for example an int member). Before substituting T into Foo, the compiler does not know if there is an specialization for the type.
The following code will not compile with G++ 4.5 or 4.6 (snapshot). It will compile with the Digital Mars Compiler 8.42n.
template <int I>
struct Foo {
template <int J>
void bar(int x) {}
};
template <int I>
void test()
{
Foo<I> a;
a.bar<8>(9);
};
int main(int argc, char *argv[]) {
test<0>();
return 0;
}
The error message is:
bugbody.cpp: In function 'void test() [with int I = 0]':
bugbody.cpp:16:11: instantiated from here
bugbody.cpp:11:3: error: invalid operands of types '<unresolved overloaded function type>' and 'int' to binary 'operator<'
Is the program valid C++?
Since the bar in a.bar is a dependent name, the compiler doesn’t know that it’s a template. You need to specify this, otherwise the compiler interprets the subsequent <…> as binary comparison operators:
a.template bar<8>(9);
The compiler behaves correctly.
The reason for this behaviour lies in specialisation. Imagine that you have specialised the Foo class for some value:
template <>
struct Foo<0> {
int bar;
};
Now your original code would compile, but it would mean something completely different. In the first parsing pass, the compiler doesn’t yet know which specialisation of Foo you’re using here so it needs to disambiguate between the two possible usages of a.bar; hence the keyword template to show the compiler that the subsequent <…> are template arguments.