Class template argument deduction with partial specialization - c++

I'm having some trouble understanding all the limitations of the new C++17 feature that allows template deduction on constructors.
In particular, this example compiles correctly:
struct B {};
template <typename T, typename = T>
struct A {
A(T) {}
};
int main() {
B b;
A a(b); // ok
}
While this one does not:
struct B {};
template <typename T, typename = T>
struct A;
template <typename T>
struct A<T> {
A(T) {}
};
int main() {
B b;
A a(b); // error
}
The error in this second case is:
main.cpp: In function ‘int main()’:
main.cpp:17:14: error: class template argument deduction failed:
A a(b);
^
main.cpp:17:14: error: no matching function for call to ‘A(B&)’
main.cpp:4:12: note: candidate: template<class T, class> A(A<T, <template-parameter-1-2> >)-> A<T, <template-parameter-1-2> >
struct A;
^
main.cpp:4:12: note: template argument deduction/substitution failed:
main.cpp:17:14: note: ‘B’ is not derived from ‘A<T, <template-parameter-1-2> >’
A a(b);
^
Why is this happening?

Class template argument deduction only considers constructors from the primary class template in order to do deduction. In the first example, we have one constructor that we synthesize a function template for:
template <class T> A<T> __f(T );
The result of __f(b) is A<B>, and we're done.
But in the second example, the primary class template is just:
template <typename T, typename = T>
struct A;
It has no constructors, so we have no function templates to synthesize from them. All we have is a hypothetical default constructor and the copy deduction guide, which together give us this overload set:
template <class T> A<T> __f();
template <class T> A<T> __f(A<T> );
Neither of which are viable for __f(b) (the compile error you get is about trying to match the copy deduction guide), so deduction fails.
If you want this to succeed, you'll have to write a deduction guide:
template <class T>
A(T ) -> A<T>;
Which would allow A a(b) to work.

Related

Using CRTP with partial class specialization?

I'm trying to mix-in an operator [] with a class. My problem is I've partially specialized the class, and the compiler doesn't like me not specifying the template parameters to the derived class:
#include <iostream>
#include <type_traits>
using namespace std;
template <typename T>
struct mixin {
template <typename U>
void operator[](U u) {
cout << u;
}
};
template <typename T, typename = void>
struct derived : mixin<derived> {};
template <typename T>
struct derived<T,
typename enable_if<
is_same<T, int>{}
>::type> : mixin<derived> {};
int main() {
derived<int> d;
d[3.14];
}
With clang this gives:
test.cc:16:24: error: use of class template 'derived' requires template arguments
struct derived : mixin<derived> {};
^~~~~~~
test.cc:16:8: note: template is declared here
struct derived : mixin<derived> {};
^
test.cc:23:22: error: use of class template 'derived' requires template arguments
>::type> : mixin<derived> {};
^~~~~~~
test.cc:16:8: note: template is declared here
struct derived : mixin<derived> {};
^
gcc is even less helpful:
test.cc:16:31: error: type/value mismatch at argument 1 in template parameter list for ‘template<class T> struct mixin’
struct derived : mixin<derived> {};
^
test.cc:16:31: note: expected a type, got ‘derived’
test.cc:23:29: error: type/value mismatch at argument 1 in template parameter list for ‘template<class T> struct mixin’
>::type> : mixin<derived> {};
^
test.cc:23:29: note: expected a type, got ‘derived’
test.cc: In function ‘int main()’:
Is my only option to re-specify the template parameters inside of the mixin clause?
Well, try this:
#include <iostream>
#include <type_traits>
using namespace std;
template <typename T>
struct mixin {
template <typename U>
void operator[](U u) {
cout << u;
}
};
template <typename T, typename = void>
struct derived : mixin<derived<T>> {};
template <typename T>
struct derived<T,
typename enable_if<
is_same<T, int>::value
>::type> : mixin<derived<T>> {};
int main() {
derived<int> d;
d[3.14];
}
It does work...
What I changed:
Using is_same<foo,bar>::value, not is_same<foo,bar>{} edit: Hmm, it seems you don't need to change that after all. Neat!
Not trying to get the compiler to deduce the template parameter for derived when using mixin<derived>. You were being way too optimistic there...

C++ - specialize function template on a templated class with a non type template parameter

I have a class template Foo:
template <class A, A value, class B>
class Foo {};
And I have a function template validateType()
template <class T>
bool validateType() {
return false;
}
Now I want to specialize it for some types, including Foo, so that the function performs some static_asserts during compile time. I tried this:
template <class A, class B, Foo<A, A val, B>>
bool validateType() {
// do some static asserts
}
and this:
template <class A, A val, class B>
bool validateType<Foo<A, val, B>>() {
// do some static asserts
}
In the first one, the compiler says:
error: wrong number of template arguments (2, should be 3)
template <class A, class B, Foo<A, A val, B>>
^~
note: provided for ‘template<class A, A value, class B> class Foo’
class Foo {};
^~~
error: two or more data types in declaration of ‘validateType’
bool validateType() {
^
error: expected ‘>’ before ‘{’ token
bool validateType() {
^
And in the second case I get
error: non-class, non-variable partial specialization ‘validateType<Foo<A, val, B> >’ is not allowed
bool validateType<Foo<A, val, B>>() {
^
How should this be done?
Partial template specializations are not allowed for function templates.
Use SFINAE or class templates
template <class T>
struct validateType : std::false_type {};
template <class A, A val, class B>
struct validateType<Foo<A, val, B>> : std::true_type {};
Edit:
Is this supposed to work for template functions as well?
NO. Partial template specializations are not allowed for function templates.
for template function, use SFINAE.
For example, this sample check weather T is unsigned integer type(C++17).
template<typename T, std::enable_if_t<std::is_unsigned_v<T>, std::nullptr_t> = nullptr>
T foo(T n);
std::is_unsigned_v was added in C++17. before C++17, use std::is_unsigned<T>::value.
https://en.cppreference.com/w/cpp/types/is_unsigned
std::enable_if_t was added in C++14. before C++14, use typename std::enable_if<con, T>::type.
https://en.cppreference.com/w/cpp/types/enable_if
std::nullptr_t can hole only one value(nullptr) so that I use it for SFINAE enabler.
(ja) https://qiita.com/kazatsuyu/items/203584ef4cb8b9e52462
However, in your case, you chould use class templates. It's simplest way to use class templates to check wether T is template class foo(BTW, not for template class foo, std::is_same is simplest way).

Template argument deduction failed, SFINAE

When I compile this code:
#include <type_traits>
template <typename T>
void do_stuff(std::enable_if_t<std::is_integral<T>::value, T> &t) {}
template <typename T>
void do_stuff(std::enable_if_t<std::is_class<T>::value, T> &t) {}
int main() {
int i = 1;
do_stuff(i);
return 0;
}
GCC says:
37325975.cpp: In function ‘int main()’:
37325975.cpp:11:15: error: no matching function for call to ‘do_stuff(int&)’
do_stuff(i);
^
37325975.cpp:4:6: note: candidate: template<class T> void do_stuff(std::enable_if_t<std::is_integral<_Tp>::value, T>&)
void do_stuff(std::enable_if_t<std::is_integral<T>::value, T> &t) {}
^
37325975.cpp:4:6: note: template argument deduction/substitution failed:
37325975.cpp:11:15: note: couldn't deduce template parameter ‘T’
do_stuff(i);
^
37325975.cpp:7:6: note: candidate: template<class T> void do_stuff(std::enable_if_t<std::is_class<T>::value, T>&)
void do_stuff(std::enable_if_t<std::is_class<T>::value, T> &t) {}
^
37325975.cpp:7:6: note: template argument deduction/substitution failed:
37325975.cpp:11:15: note: couldn't deduce template parameter ‘T’
do_stuff(i);
^
I've also tried on msvc 2013.
Why do I get these errors?
Live Demo
As the compiler says, that parameter type is non-deducible, so you would need to supply the template argument manually, like this:
do_stuff<int>(i);
A better option is to put the std::enable_if in the return type or template parameter list:
//Return type
template <typename T>
std::enable_if_t<std::is_integral<T>::value>
do_stuff(T &t) {}
template <typename T>
std::enable_if_t<std::is_class<T>::value>
do_stuff(T &t) {}
//Parameter list
template <typename T, std::enable_if_t<std::is_integral<T>::value>* = nullptr>
void do_stuff(T &t) {}
template <typename T, std::enable_if_t<std::is_class<T>::value>* = nullptr >
void do_stuff(T &t) {}
This way the template parameter can still be deduced:
do_stuff(i);
Why do I get these errors?
Because template argument deduction fails for nested-name-specifier, which is non-deduced contexts.
The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id.
// the identity template, often used to exclude specific arguments from deduction
template<typename T> struct identity { typedef T type; };
template<typename T> void bad(std::vector<T> x, T value = 1);
template<typename T> void good(std::vector<T> x, typename identity<T>::type value = 1);
std::vector<std::complex<double>> x;
bad(x, 1.2); // P1 = std::vector<T>, A1 = std::vector<std::complex<double>>
// P1/A1: deduced T = std::complex<double>
// P2 = T, A2 = double
// P2/A2: deduced T = double
// error: deduction fails, T is ambiguous
good(x, 1.2); // P1 = std::vector<T>, A1 = std::vector<std::complex<double>>
// P1/A1: deduced T = std::complex<double>
// P2 = identity<T>::type, A2 = double
// P2/A2: uses T deduced by P1/A1 because T is to the left of :: in P2
// OK: T = std::complex<double>
When the compiler tries to resolve do_stuff(int&), it sees the two candidates that the compiler tells you about. But it's not able to "work backwards" to find a T that satisfies std::enable_if_t<std::is_integral<T>::value, T> == int, nor to find a T that satisfies std::enable_if_t<std::is_class<T>::value, T> == int.
As mentioned in TartanLlama's answer, the way to avoid this is to make the argument deducible (e.g. do_stuff(T&)) and make the return type or a subsequent template argument dependent on T.

"ambiguating new declaration" error for a templated method in a templated class

I have written the following earth-shattering application:
class SomeA { }; class SomeB { }; class SomeC { };
template <typename A, typename B, typename... Cs>
class Foo {
public:
template <typename U> static void bar();
};
template <typename U>
void Foo<SomeA, SomeB, SomeC>::bar() { };
int main() { return 0; }
When I compile this (gcc 4.9.3 with -std=c++11), I get the following error:
a.cpp:10:36: error: ambiguating new declaration of ‘static void Foo<SomeA, SomeB, SomeC>::bar()’
void Foo<SomeA, SomeB, SomeC>::bar() { };
^
a.cpp:6:36: note: old declaration ‘static void Foo<A, B, Cs>::bar() [with U = U; A = SomeA; B = SomeB; Cs = {SomeC}]’
template <typename U> static void bar();
^
Why is this an "ambiguating declaration", and how else can I implement bar for all Us but for a specific instantiation of Foo?
With clang 3.6.2, I get the error:
a.cpp:9:1: error: template parameter list matching the non-templated nested type 'Foo<SomeA, SomeB, SomeC>' should be empty ('template<>')
template <typename U>
^ ~~~~~~~~~~~~
I don't really get this either. How am I supposed to template over U if clang wants an empty parameter list?
No idea what ambiguating new declaration means, but you're specializing the enclosing class template Foo, so you need to indicate that with an empty template parameter list
template <>
template <typename U>
void Foo<SomeA, SomeB, SomeC>::bar() { }
Live demo

partial class template specialization with multiple arguments

I have a template class Foo that takes two (or more) template arguments. I want to use its type in a separate class Bar. See the following simple example, which compiles without error:
template <typename T, typename U> class Foo { };
template <typename T, typename U> class Bar { };
int main()
{
Bar<int, char> bar; // quick example -- int & char could be any 2 types
return 0;
}
The above is somewhat tedious, especially if Foo takes many template arguments and the programmer has to retype them all. I would like to have something like the following instead, but it does not compile:
template <typename T, typename U> class Foo { };
template <typename T, typename U> class Bar; // base
template <typename T, typename U> class Bar< Foo<T, U> > { }; // specialization
int main()
{
typedef Foo<int, char> FooType;
Bar<FooType> bar;
return 0;
}
test.cpp:3:60: error: wrong number of template arguments (1, should be 2)
test.cpp:2:45: error: provided for ‘template class Bar’
test.cpp: In function ‘int main()’:
test.cpp:7:18: error: wrong number of template arguments (1, should be 2)
test.cpp:2:45: error: provided for ‘template class Bar’
test.cpp:7:23: error: invalid type in declaration before ‘;’ token
I am especially perplexed because this partial specialization idiom works fine for a single template argument; see the question titled: total class specialization for a template
Edit I realized that, at least for my purposes, I could get around this using C++11 variadic templates as follows. I still want to know why the second example doesn't work, though.
template <typename... FooTypes> class Bar;
template <typename... FooTypes> class Bar< Foo<FooTypes...> > { };
Your class template Bar<T, U> takes two template arguments, but your specialization is only given one:
template <typename T, typename U> class Bar<Foo<T, U> > {};
Did you mean to have Bar take just one template argument and specialize it correspondingly?
template <typename T> class Bar;
template <typename T, typename U> class Bar<Foo<T, U> > {};
Note that a specialization can depend on a different number of template parameters but the specialization needs to get the same number of arguments. It also works the other way around: a full specialization can have no template parameter:
template <> class Bar<int> {};
I'm a little confused about what you're trying to do in this line:
template <typename T, typename U> class Bar< Foo<T, U> > { }; // specialization
You're stating that the template requires two types, T and U, as type parameters. Foo is itself only one type though, the type created by instantiating the Foo template with those two arguments.
I see that you're expecting it to pick up and determine the T and U since you used them in both places, but that doesn't circumvent the fact that you only provided one type argument for a two type template specialization.