I have a class A which contains a templated member B whose exact type should be deduced from A's constructor. The way this is supposed to work is that, as shown in the below example, B can be instantiated with either 1 or 2 parameters to its constructor (deduction guide will tell) but will take a const char* in any case. When I instantiate A with the const char* argument for the constructor, an object B should be instantiated from the const char* as A only takes a B object. However, this is how far I get:
#include <iostream>
template <bool LengthOpt>
struct B
{
B(const char*) { }
B(const char*, size_t) { }
void print() {
if constexpr (LengthOpt) {
std::cout << "LengthOpt is set" << std::endl;
}
}
};
B(const char*) -> B<false>;
B(const char*, size_t) -> B<true>;
template <template <bool LengthOpt> class T>
struct A
{
A(T is) : is_{is} {
}
void print() {
is_.print();
}
T is_;
};
int main()
{
A a("hello");
a.print();
}
And it yields those errors:
<source>:24:7: error: use of template template parameter 'T' requires template arguments; argument deduction not allowed in function prototype
A(T is) : is_{is} {
^
<source>:21:43: note: template is declared here
template <template <bool LengthOpt> class T>
^
<source>:32:5: error: use of template template parameter 'T' requires template arguments; argument deduction not allowed in non-static struct member
T is_;
^
<source>:21:43: note: template is declared here
template <template <bool LengthOpt> class T>
^
<source>:37:7: error: no viable constructor or deduction guide for deduction of template arguments of 'A'
A a("hello");
^
<source>:22:8: note: candidate template ignored: could not match 'A<T>' against 'const char *'
struct A
^
<source>:22:8: note: candidate function template not viable: requires 0 arguments, but 1 was provided
My take on the problem is that the compiler doesn't know that I want to instantiate an object B in A's constructor, as the template template argument specifies nothing. It could very well be just any object that takes one template parameter.
I'm scratching my head right now on how to resolve this. Is it even possible or am I scratching a limitation in C++ again?
The following code fails to compile (Godbolt link):
#include <concepts>
template <class Fn>
decltype(auto) g(Fn&& fn) { return fn(); }
template <typename T>
requires(std::integral<T>) int f() { return 0; }
template <typename T>
int f() { return 1; }
int main() {
f<int>();
f<void>();
g(f<int>); // error: invalid initialization of non-const reference of type 'int (&)()'
// from an rvalue of type '<unresolved overloaded function type>'
g(f<void>);
}
It seems unexpected to me that the overload resolution succeeds when calling f<int>() (selecting the constrained version as a better match than the unconstrained version) but fails when passing f<int> as an argument.
Note that changing the unconstrained version to a disjoint constraint does make it compile (Godbolt link):
#include <concepts>
template <class Fn>
decltype(auto) g(Fn&& fn) { return fn(); }
template <typename T>
requires(std::integral<T>) int f() { return 0; }
template <typename T>
requires(!std::integral<T>) int f() { return 1; }
int main() {
f<int>();
f<void>();
g(f<int>);
g(f<void>);
}
So is the compiler behavior correct? And if so, is this an inconsistency in the standard, or is it intended to work this way?
It seems that neither GCC nor Clang has fully implemented the rules for forming pointers to constrained functions: [over.over]/5 definitely considers constraint ordering in choosing an overload. There were some late changes to these, although they’re just as relevant to the disjoint-constraints case as to the unconstrained case.
I have this code:
struct My
{
typedef int foo;
};
struct My2
{
};
template <typename T>
void Bar(const T&, int z = typename T::foo())
{
std::cout << "My" << std::endl;
}
void Bar(...)
{
std::cout << "..." << std::endl;
}
int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Compile error: no type named ‘foo’ in ‘struct My2’
return 0;
}
I suppose, that if some class T doesn't have typedef foo inside, compiler should exclude first overload and choose overload with ellipsis. But I check this code on MSVC, gcc and clang and I get compile error on those compilers. Why SFINAE doesn't work in this case?
The type of z is not subject to template substitution, it is always int. This means there is no opportunity for SFINAE, and you instead get a compiler error when attempting to resolve T::foo for the default value. Default arguments do not participate in overload resolution, instead being instantiated only when missing from the function call. Section 14.7.1 (paragraphs 13/14) of the standard describes this behaviour, but does not give justification for the lack of SFINAE here.
SFINAE can be allowed to happen by making the type of z a template parameter, as below:
(live example: http://ideone.com/JynMye)
#include <iostream>
struct My
{
typedef int foo;
};
struct My2
{
};
template<typename T, typename I=typename T::foo> void Bar(const T&, I z = I())
{
std::cout << "My\n";
}
void Bar(...)
{
std::cout << "...\n";
}
int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Also OK
return 0;
}
This will use the "My" version for the first call, and the "..." version for the second call. The output is
My
...
However, if void Bar(...) was a template, for whatever reason, the "My" version will never get a chance:
(live example: http://ideone.com/xBQiIh)
#include <iostream>
struct My
{
typedef int foo;
};
struct My2
{
};
template<typename T, typename I=typename T::foo> void Bar(const T&, I z = I())
{
std::cout << "My\n";
}
template<typename T> void Bar(T&)
{
std::cout << "...\n";
}
int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Also OK
return 0;
}
Here, the "..." version is called in both cases. The output is:
...
...
One solution is to use class template (partial) specialisation; provide the "..." version as the base, with the type of the second parameter defaulted to int, and the "My" version as a specialisation where the second parameter is typename T::foo. In conjunction with a plain template function to deduce T and dispatch to the appropriate class' member function, this produces the desired effect:
(live example: http://ideone.com/FanLPc)
#include <iostream>
struct My
{
typedef int foo;
};
struct My2
{
};
template<typename T, typename I=int> struct call_traits {
static void Bar(...)
{
std::cout << "...\n";
}
};
template<typename T> struct call_traits<T, typename T::foo> {
static void Bar(const T&, int z=typename T::foo())
{
std::cout << "My\n";
}
};
template<typename T> void Bar(const T& t)
{
call_traits<T>::Bar(t);
}
int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Still OK
return 0;
}
Here, the output is:
My
...
The type z is an int, is not being deduced by the compiler, no room for SFINAE to take place. The value being used to initialise z is based on the default of T::foo, which doesn't exist; hence the error.
If the type for z is elevated to the template itself, substitution can now fail, and SFINAE kicks in.
#include <iostream>
struct My
{
typedef int foo;
};
struct My2
{
};
template <typename T, typename I = typename T::foo>
void Bar(const T&, I z = I())
{
(void)z; // silence any warnings on unused
std::cout << "My" << std::endl;
}
void Bar(...)
{
std::cout << "..." << std::endl;
}
int main()
{
My my;
Bar(my);
My2 my2;
Bar(my2); // Compiles
return 0;
}
Live sample
In order for a function template to be part of the overloaded list of candidate functions, the template argument deduction must succeed. If it fails, then the candidate is removed from the list. Hence, if no deduction failure occurs, it is added to the candidate list (but this does not preclude further errors if it is finally selected).
14.8.3/1 Overload resolution
A function template can be overloaded either by (non-template) functions of its name or by (other) function templates of the same name. When a call to that name is written (explicitly, or implicitly using the operator notation), template argument deduction (14.8.2) and checking of any explicit template arguments (14.3) are performed for each function template to find the template argument values (if any) that can be used with that function template to instantiate a function template specialization that can be invoked with the call arguments. For each function template, if the argument deduction and checking succeeds, the template arguments (deduced and/or explicit) are used to synthesize the declaration of a single function template specialization which is added to the candidate functions set to be used in overload resolution. If, for a given function template, argument deduction fails, no such function is added to the set of candidate functions for that template. The complete set of candidate functions includes all the synthesized declarations and all of the non-template overloaded functions of the same name. The synthesized declarations are treated like any other functions in the remainder of overload resolution, except as explicitly noted in 13.3.3.
Template argument deduction is performed on the function type and its template arguments themselves.
14.8.2/8 Template argument deduction
If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguments. [ Note: If no diagnostic is required, the program is still ill-formed. Access checking is done as part of the substitution process. —end note ] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure.
From the OP, the function Bar<T> is added to the candidate list since it can be deduced what the type for T is. It is instantiated and the default arguments are checked, and hence it fails.
14.7.1/13 Implicit instantiation
If a function template f is called in a way that requires a default argument to be used, the dependent names are looked up, the semantics constraints are checked, and the instantiation of any template used in the default argument is done as if the default argument had been an initializer used in a function template specialization with the same scope, the same template parameters and the same access as that of the function template f used at that point. This analysis is called default argument instantiation. The instantiated default argument is then used as the argument of f.
Quotes taken from draft n3797
One more C++03 compatible option for you. Because in answers above default argument was used in template function and it is not permitted in standard.
#include <iostream>
struct TypeWithFoo{
typedef int Foo;
};
template<typename T, bool>
struct onFooAction;
template<typename T>
struct onFooAction<T, false>{
void operator ()(const T &t){
std::cout << "No foo :(\n";
}
};
template<typename T>
struct onFooAction<T, true>{
void operator ()(const T &t){
std::cout << "Foo =)\n";
}
};
template<typename T>
struct hasFoo{
typedef char yes[1];
typedef char no[2];
template<typename C>
static yes& testForFoo(typename C::Foo*);
template<typename>
static no& testForFoo(...);
static const bool value = sizeof(testForFoo<T>(0)) == sizeof(yes);
};
template<typename T>
void bar(const T &t){
onFooAction<T, hasFoo<T>::value>()(t);
}
int main(){
bar(10);
bar(TypeWithFoo());
}
I want to be able to specialize the ctor of a class the following way:
template<typename T>
class Foo {
public:
template<typename... Ts>
Foo(Ts... & args) {
// ...
}
template<>
Foo(int i) {
// ...
}
};
I get the following error:
error: explicit specialization in non-namespace scope ‘class Foo’
If I try to move the specialization outside the class, like this:
template<typename T>
class Foo {
public:
template<typename... Ts>
Foo(Ts &... args) {
// ...
}
};
template<typename T>
template<int>
Foo<T>::Foo(int i) {
// ...
}
I get the following errors:
error: prototype for ‘Foo::Foo(int)’ does not match any in class
‘Foo’
error: candidate is: template template
Foo::Foo(Ts& ...)
How do I do this correctly?
You can just overload the constructor instead:
template<typename T>
class Foo {
public:
template<typename... Ts>
Foo(Ts&... args) {
// ...
}
// template<> <- REMOVE THIS
Foo(int i) {
// ...
}
};
Overload resolution will prefer the non-template overload so doing Foo<MyType> f(1234); would choose Foo<MyType>::Foo(int);.
LIVE EXAMPLE (I've modified the variadic to be const for it to accept temporaries for the sake of the example).
Note that the position of the type modifier in your variadic function is wrong. It should be with the type, on the left side of ...:
Foo(Ts&... args)
member function and by extension constructors are not specialize-able without specializing the outer template completely.
Just write the ctor with an int not template will works here.
14.7.3p18: "In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope,
the member template and some of its enclosing class templates may
remain unspecialized, except that the declaration shall not explicitly
specialize a class member template if its enclosing class templates
are not explicitly specialized as well."
I have no problem passing the address of a function template specialization to a regular template function:
template <typename T>
void f(T) {}
template <typename A, typename B>
void foo(A, B) {}
int main()
{
foo(&f<int>, &f<float>);
}
However, when I try to pass the same specializations to a variadic template:
template <typename T>
void f(T) {}
template <typename... A>
void bar(A...) {}
int main()
{
bar(&f<int>, &f<float>);
}
I get the following compiler errors with GCC (I tried 4.6.1 and 4.7.0):
test.cpp: In function 'int main()':
test.cpp:9:27: error: no matching function for call to 'bar(<unresolved overloaded function type>, <unresolved overloaded function type>)'
test.cpp:9:27: note: candidate is:
test.cpp:5:6: note: template<class ... A> void bar(A ...)
test.cpp:5:6: note: template argument deduction/substitution failed:
Why am I getting these errors?
Looks like it might be a bug in GCC that is possibly fixed in GCC 4.6.2 (I say possibly because it's not exactly the same, but does involve getting the address of a variadic template function).