I can't seem to wrap my head around what is going wrong with the following example
$ cat repro.cpp
#include <iostream>
using namespace std;
template<class T>
struct S1_t
{
template<class U>
struct inner_t_x {};
using inner_t = inner_t_x<T>;
};
struct Bogus{};
template<class T>
struct Functor1
{
void operator()()
{
cout << "Functor1 FAIL: " << __PRETTY_FUNCTION__ << endl;
}
};
template<>
struct Functor1<typename S1_t<Bogus>::template inner_t_x<Bogus>>
{
void operator()()
{
cout << "Functor1 PASS: " << __PRETTY_FUNCTION__ << endl;
}
};
template<class T>
struct Functor2
{
void operator()()
{
cout << "Functor2 FAIL: " << __PRETTY_FUNCTION__ << endl;
}
};
template<class T>
struct Functor2<typename S1_t<T>::template inner_t_x<T>>
{
void operator()()
{
cout << "Functor2 PASS: " << __PRETTY_FUNCTION__ << endl;
}
};
template<class T>
void eval()
{
Functor1<T>{}();
Functor2<T>{}();
}
int main()
{
eval<S1_t<Bogus>::inner_t>();
return 0;
}
$ clang++ repro.cpp -std=c++11 -Wall && ./a.out
Functor1 PASS: void Functor1<S1_t<Bogus>::inner_t_x<Bogus> >::operator()()
Functor2 FAIL: void Functor2<S1_t<Bogus>::inner_t_x<Bogus> >::operator()() [T = S1_t<Bogus>::inner_t_x<Bogus>]
In the Functor1, I explicitly specialise on the type Bogus, and indeed that specialisation is being invoked. However, in the Functor2 the type is allowed to be deduced, but partial specialisation fails and generic template is instantiated instead. Yet, __PRETTY_PRINT__ show the same signature for Functor1 & Functort2 when instantiated.
Can anyone able to explain this behaviour, and is there a way to fix Functor2 partial specialisation to fit this scenario? Thanks!
fwiw: Changing Functor2 partial specialisation to
template<class T>
struct Functor2<typename S1_t<Bogus>::template inner_t_x<T>>
{
void operator()()
{
cout << "Functor2 PASS: " << __PRETTY_FUNCTION__ << endl;
}
};
gives correct output
Functor1 PASS: void Functor1<S1_t<Bogus>::inner_t_x<Bogus> >::operator()()
Functor2 PASS: void Functor2<S1_t<Bogus>::inner_t_x<Bogus> >::operator()() [T = S1_t<Bogus>::inner_t_x<Bogus>]
but this is not the option in my use case.
typename S1_t<T>::..is not deducible in partial specialization.
You got a clearer error when using inner_t instead of inner_t_x<T>:
main.cpp:41:8: error: template parameters not deducible in partial specialization:
struct Functor2<typename S1_t<T>::inner_t>
^
main.cpp:41:8: note: 'T'
main.cpp:41:8: warning: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used
The specialization of Functor2 will never be used. The compiler could issue a warning about that, but it doesn't in your case. The reason for it being not deducible is simple: Imagine you later add something like
struct Hogus;
template<>
struct S1_t<Hogus>
{
template <typename U>
using inner_t_x = S1_t<Bogus>::inner_t_x<Bogus>;
};
then S1_t<Bogus>::inner_t_x<Bogus> and S1_t<Hogus>::inner_t_x<Hogus> would be the same type. Therefore, the template deduction in the partial specialization of Functor2 could yield both T=Bogus or T=Hogus. Hence, it cannot be deduced unambiguously under any circumstances.
Related
I have a code, which compiles successfully (g++ 4.9.2):
#include <iostream>
#include <utility>
// general function for any variadic templated argument
template<template<typename ...> class T, typename ...TTs>
void
foo(T<TTs...>& arg)
{
std::cout << "T<TTs...>" << std::endl;
}
template<typename ...Ts>
struct xxx
{
// not important
};
// specialization for only variadic templated xxx
template<typename ...TTs> void
foo(xxx<TTs...>& arg)
{
std::cout << "xxx<TTs...>" << std::endl;
}
// specialization for non-variadic templated xxx
template<typename TT> void
foo(xxx<TT>& arg)
{
std::cout << "xxx<TT>" << std::endl;
}
// specialization for xxx<uint8_t>
template<> void
foo(xxx<uint8_t>& arg)
{
std::cout << "xxx<uint8_t>" << std::endl;
}
int
main(int argc, char** argv)
{
xxx<uint8_t> x1;
std::cout << "xxx<uint8_t> => ";
foo(x1);
xxx<uint16_t> x2;
std::cout << "xxx<uint16_t> => ";
foo(x2);
xxx<uint8_t,uint16_t> x3;
std::cout << "xxx<uint8_t,uint16_t> => ";
foo(x3);
std::pair<uint8_t,uint16_t> x4;
std::cout << "std::pair<uint8_t,uint16_t> => ";
foo(x4);
return 0;
}
and produces:
xxx<uint8_t> => xxx<uint8_t>
xxx<uint16_t> => xxx<TT>
xxx<uint8_t,uint16_t> => xxx<TTs...>
std::pair<uint8_t,uint16_t> => T<TTs...>
Now I want to have these foo methods inside a class, and write:
#include <iostream>
#include <utility>
class abc
{
public:
// general function for any variadic templated argument
template<template<typename ...> class T, typename ...TTs>
void
foo(T<TTs...>& arg)
{
std::cout << "T<TTs...>" << std::endl;
}
};
template<typename ...Ts>
struct xxx
{
// not important
};
// specialization for only variadic templated xxx
template<typename ...TTs> void
abc::foo(xxx<TTs...>& arg)
{
std::cout << "xxx<TTs...>" << std::endl;
}
// specialization for non-variadic templated xxx
template<typename TT> void
abc::foo(xxx<TT>& arg)
{
std::cout << "xxx<TT>" << std::endl;
}
// specialization for xxx<uint8_t>
template<> void
abc::foo(xxx<uint8_t>& arg)
{
std::cout << "xxx<uint8_t>" << std::endl;
}
int
main(int argc, char** argv)
{
abc p;
xxx<uint8_t> x1;
std::cout << "xxx<uint8_t> => ";
p.foo(x1);
xxx<uint16_t> x2;
std::cout << "xxx<uint16_t> => ";
p.foo(x2);
xxx<uint8_t,uint16_t> x3;
std::cout << "xxx<uint8_t,uint16_t> => ";
p.foo(x3);
std::pair<uint8_t,uint16_t> x4;
std::cout << "std::pair<uint8_t,uint16_t> => ";
p.foo(x4);
return 0;
}
and this produces a compilation error:
test_ko.cc:24:1: error: prototype for ‘void abc::foo(xxx<TTs ...>&)’ does not match any in class ‘abc’
abc::foo(xxx<TTs...>& arg)
^
test_ko.cc:10:5: error: candidate is: template<template<class ...> class T, class ... TTs> void abc::foo(T<TTs ...>&)
foo(T<TTs...>& arg)
^
test_ko.cc:31:1: error: prototype for ‘void abc::foo(xxx<TT>&)’ does not match any in class ‘abc’
abc::foo(xxx<TT>& arg)
^
test_ko.cc:10:5: error: candidate is: template<template<class ...> class T, class ... TTs> void abc::foo(T<TTs ...>&)
foo(T<TTs...>& arg)
I want a specialization for foo methods, not declaring a new signature inside abc class (because there is a class with templates, a part of a library, and separate specialization, for later declared xxx class).
It looks for me that code with methods is similar to one with functions, but I get error here. What I do wrong?
// general function for any variadic templated argument
template<template<typename ...> class T, typename ...TTs>
void
foo(T<TTs...>& arg)
{
std::cout << "T<TTs...>" << std::endl;
}
This is a template function.
template<typename ...Ts>
struct xxx
{
// not important
};
// specialization for only variadic templated xxx
template<typename ...TTs> void
foo(xxx<TTs...>& arg)
{
std::cout << "xxx<TTs...>" << std::endl;
}
This is not a specialization. This is a different template function whose name foo overloads with the above template function.
// specialization for non-variadic templated xxx
template<typename TT> void
foo(xxx<TT>& arg)
{
std::cout << "xxx<TT>" << std::endl;
}
This is not a specialization. This is a different template function whose name foo overloads with the above template functions.
// specialization for xxx<uint8_t>
template<> void
foo(xxx<uint8_t>& arg)
{
std::cout << "xxx<uint8_t>" << std::endl;
}
This is a full specialization of one of the above template functions. I think the 3rd one, but I wouldn't bet money on it. (I believe it specializes whichever template xxx<uint8_t>& would be dispatched to if you called foo).
Instead of this full specialization, I would instead write:
inline foo(xxx<uint8_t>& arg)
{
std::cout << "xxx<uint8_t>" << std::endl;
}
which is a brand new overload of foo again. Overloads are far less quirky than full function specializations.
There is no such thing as partial template function specialization.
This explains why your attempt to specialize methods with the same syntax doesn't work. There is no such thing as partial template member function specialization either.
You either have to write the overloads in the class itself, or dispatch to a different context.
What "went wrong" and was the proximate cause of the error is that your initial code introduced new overloads. You are not permitted to introduce new overloads of a method outside of the class definition, so the compiler pointed out your error.
Here is a useful technique. We are in abc:
// general function for any variadic templated argument
template<template<typename ...> class T, typename ...TTs>
void foo(T<TTs...>& arg)
{
return foo(*this, arg);
}
private:
template<template<typename ...> class T, typename ...TTs>
friend void foo(abc& self, T<TTs...>& arg)
{
std::cout << "T<TTs...>" << std::endl;
}
Our foo method extends to a foo friend.
We then add code in the same namespace as abc:
template<typename ...TTs> void
foo(abc& self, xxx<TTs...>& arg)
{
std::cout << "xxx<TTs...>" << std::endl;
}
template<typename TT> void
foo(abc& self, xxx<TT>& arg)
{
std::cout << "xxx<TT>" << std::endl;
}
inline void foo(abc& self, xxx<uint8_t>& arg)
{
std::cout << "xxx<uint8_t>" << std::endl;
}
and they are found via ADL when abc::foo is called.
I try to use std::enable_if with an unused and unnamed type parameter, in order to not distort the return type. However, the following code does not compile.
#include <iostream>
template <typename T, typename = std::enable_if_t<!std::is_integral<T>::value>>
T foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
T foo() { std::cout << "integral" << std::endl; return T(); }
int main() {
foo<float>();
foo<int>();
}
The compiler says:
7:3: error: redefinition of 'template<class T, class> T foo()'
4:3: note: 'template<class T, class> T foo()' previously declared here
In function 'int main()':
11:12: error: no matching function for call to 'foo()'
11:12: note: candidate is:
4:3: note: template<class T, class> T foo()
4:3: note: template argument deduction/substitution failed:
What is the problem here? How do I have to change the code to get it compile? The text book "Discovering Modern C++" explicitly encourages the use of std::enable_if with anonymous type parameters.
EDIT: I know that it works if I put std::enable_if into the return type. However, my intention is to get some more details why it does not work if I use it with anonymous type parameters. As I said, my text book encourages the variant using anonymous type parameters, so I am wondering why my code does not compile.
However, my intention is to get some more details why it does not work if I use it with anonymous type parameters.
Default values do not participate in overload resolution, thus you are actually redefining the same function.
Let's simplify your example:
template<typename = int>
void f() {}
template<typename = void>
void f() {}
int main() {
f<>();
}
The code above does not compile, for it couldn't know what version of f you want to invoke.
In your case, if I invoke foo as foo<void, void>, I've almost the same problem.
The compiler cannot guess what's my intention and the fact that the second parameter has a default value doesn't mean that you can't pass in a different type.
Because of that, the code is ill-formed and the compiler correctly gives you an error.
As a side note, you can still have it working without using the std::enable_if_t in the return type.
As an example:
#include <type_traits>
#include <iostream>
template <typename T, std::enable_if_t<!std::is_integral<T>::value>* = nullptr>
T foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T, std::enable_if_t<std::is_integral<T>::value>* = nullptr>
T foo() { std::cout << "integral" << std::endl; return T(); }
int main() {
foo<float>();
foo<int>();
}
While I tried to figure out what was the (wrong) assumption of the OP and explain why it can be the case, #T.C. correctly pointed the attention out to the actual reason in the comments to this answer.
It's worth to quote his comment to add more details to the answer:
It's not overload resolution; it's declaration matching. There are no two overloads in the first place for any ambiguity to arise. It's two redefinition errors: the function template, and the default template argument.
You can put enable_if into the return type:
template <typename T>
std::enable_if_t<!std::is_integral<T>::value,T>
foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T>
std::enable_if_t<std::is_integral<T>::value, T>
foo() { std::cout << "integral" << std::endl; return T(); }
By the way, enable_if_t is available from C++14, so you might want to say typename std::enable_if<std::is_integral<T>::value, T>::type instead. Quite a mouthful.
But a little more idiomatic (and readable) would be to dispatch basing on the type:
template <typename T>
T foo_impl(std::false_type) { std::cout << "non-integral" << std::endl; return T(); }
template <typename T>
T foo_impl(std::true_type) { std::cout << "integral" << std::endl; return T(); }
template <typename T>
T foo(){
return foo_impl<T>(typename std::is_integral<T>::type{});
}
There are a couple of ways you can SFINAE away functions. You should usually refrain from adding an extra function/template parameter and just mingle with the return type.
template <typename T>
auto foo() -> std::enable_if_t<!std::is_integral<T>::value, T>
{ std::cout << "non-integral" << std::endl; return T(); }
template <typename T>
auto foo() -> std::enable_if_t<std::is_integral<T>::value, T>
{ std::cout << "integral" << std::endl; return T(); }
Your error is that you're using enable_if_t on the right of the equal sign.
You have to use it on the left
#include <iostream>
#include <type_traits>
template <typename T, std::enable_if_t<!std::is_integral<T>::value, int> = 0>
T foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>
T foo() { std::cout << "integral" << std::endl; return T(); }
int main() {
foo<float>();
foo<int>();
}
But this work with C++14.
In C++11 (your question is tagged C++11) you don't have enable_if_t.
The code become
#include <iostream>
#include <type_traits>
template <typename T,
typename std::enable_if<!std::is_integral<T>::value, int>::type = 0>
T foo() { std::cout << "non-integral" << std::endl; return T(); }
template <typename T,
typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
T foo() { std::cout << "integral" << std::endl; return T(); }
int main() {
foo<float>();
foo<int>();
}
I'm trying to specialize a member function template for two different types of classes as follows:
#include <iostream>
#include <boost/utility/enable_if.hpp>
struct Wibble
{
static const bool CAN_WIBBLE = true;
};
struct Wobble
{
static const bool CAN_WIBBLE = false;
};
struct Foo
{
//template<typename T> // Why isn't this declaration sufficient?
//void doStuff();
template<typename T>
typename boost::enable_if_c<T::CAN_WIBBLE,void>::type
doStuff();
template<typename T>
typename boost::enable_if_c<!T::CAN_WIBBLE,void>::type
doStuff();
};
template<typename T>
typename boost::enable_if_c<T::CAN_WIBBLE,void>::type
Foo::doStuff()
{
std::cout << "wibble ..." << std::endl;
}
template<typename T>
typename boost::enable_if_c<!T::CAN_WIBBLE,void>::type
Foo::doStuff()
{
std::cout << "I can't wibble ..." << std::endl;
}
int main()
{
Foo f;
f.doStuff<Wibble>();
f.doStuff<Wobble>();
}
Whereas GCC 4.8.2 compiles the code, VS .NET 2008 spits out the error message:
error C2244: 'Foo::doStuff' : unable to match function definition to an existing declaration
definition
'boost::enable_if_c<!T::CAN_WIBBLE,void>::type Foo::doStuff(void)'
existing declarations
'boost::enable_if_c<!T::CAN_WIBBLE,void>::type Foo::doStuff(void)'
'boost::enable_if_c<T::CAN_WIBBLE,void>::type Foo::doStuff(void)'
I suggest to use tag dispatching: https://ideone.com/PA5PTg
struct Foo
{
template<bool wibble>
void _doStuff();
public:
template<typename T>
void doStuff()
{
_doStuff<T::CAN_WIBBLE>();
}
};
template<>
void Foo::_doStuff<true>() { std::cout << "wibble ..." << std::endl; }
template<>
void Foo::_doStuff<false>() { std::cout << "I can't wibble ..." << std::endl; }
You can't partially specialize (member) function templates. End of story.
Even if you could, you should have had a SFINAE-friendly primary template. In pseudo code:
template<typename T, typename Enable> void doStuff();
template<typename T> void doStuff<T, typename boost::enable_if_c<T::CAN_WIBBLE,void>::type>()
{ std::cout << "wibble ..." << std::endl; }
template<typename T> void doStuff<T, typename boost::enable_if_c<!T::CAN_WIBBLE,void>::type>()
{ std::cout << "I can't wibble ..." << std::endl; }
You could still use this technique if you are ready class templates (as functors or just types defining non-template methods...).
As a rule of thumb, for function templates, overload resolution provides static polymorphism that removes the need for partial specialization. See
GotW #49 Template Specialization and Overloading
Why Not Specialize Function Templates?
Both by Herb Sutter
Why is it that this compiles:
class Testable {
public:
template <bool flag>
typename std::enable_if<flag>::type
void foo() { cout << "Yay" << endl; }
template <bool flag>
typename std::enable_if<!flag>::type
void foo() { cout << "Nay" << endl; }
};
But not if I define both foos using default types like so:
template <bool flag, typename = typename std::enable_if<flag>::type>
void foo() { cout << "Yay" << endl; } // (A)
template <bool flag, typename = typename std::enable_if<!flag>::type>
void foo() { cout << "Nay" << endl; } // (B)
I get this error (first line pointing to the definition of (B), second one pointing to (A)):
error: 'template<bool flag, class> void Testable::foo()' cannot be overloaded
error: with 'template<bool flag, class>> void Testable::foo()'
The compiler complains because the two function templates have the same signature. Paragraph 1.3.18 of the C++11 Standard specifies what the signature of a function template is defined by:
<function template> name, parameter type list (8.3.5), enclosing namespace (if any), return type, and
template parameter list
As you can see, default template arguments are not part of the signature.
You could work around this problem by changing your definition of Testable as follows:
class Testable {
public:
template <bool flag, typename std::enable_if<flag>::type* = nullptr>
void foo() { cout << "Yay" << endl; } // (A)
template <bool flag, typename std::enable_if<!flag>::type* = nullptr>
void foo() { cout << "Nay" << endl; } // (B)
};
As noted, two functions cannot have the same signature.
However, I have a different fix.
template<std::size_t>
struct secret_enum { enum class type {}; };
template<bool b, std::size_t n=0>
using EnableIf = typename std::enable_if< b, typename secret_enum<n>::type >::type;
class Testable {
public:
template <bool flag, EnableIf<flag, 0>...>
void foo() { cout << "Yay" << endl; } // (A)
template <bool flag, EnableIf<!flag, 1>...>
void foo() { cout << "Nay" << endl; } // (B)
};
where you enumerate your overloads with 0, 1, etc, which generates a possible type, which you then ... to say "0 or more of these", and the types generated are basically impossible to generate enum instances.
Sadly, this doesn't work in clang 3.2. It does in gcc 4.8.
In the below code snippet,
template<typename T1>
void func(T1& t)
{
cout << "all" << endl;
}
template<typename T2>
void func(T2 &t)
{
cout << "float" << endl;
}
// I do not want this
// template<> void func(float &t)
int main()
{
int i; float f;
func(i); // should print "all"
func(f); // should print "float"
return 0;
}
I would like to have the templates modified which by passing any type other than float will print "all" and passing float will print "float". I do not want template specialization, instead have partial specialization which will act accordingly based on input type. How should i go about it. Thanks in advance.
Well the scenario, i'm currently facing is like,
I need to have the following defined,
template<typename T1>
void func(T1 &t)
{
cout << "t1" << endl;
}
template<typename T2>
void func(T2 &t)
{
cout << "t2" << endl;
}
The following calls should print "t2"
func(int) // print "t2"
func(float) // print "t2"
func(string) // print "t2"
The following calls should print "t1"
func(char) // print "t1"
func(xyz) // print "t1"
...
func(abc) // print "t1"
some kind of grouping like the above where few should call the partial specialization implementation and others should call the default implementation.
You can combine function overloading with templates. So:
#include <iostream>
template<typename T>
void func(T& t)
{
std::cout << "all" << std::endl;
}
void func(float& f)
{
std::cout << "float" << std::endl;
}
int main()
{
int i; float f;
func(i); // prints "all"
func(f); // prints "float"
return 0;
}
Write a type traits class for your condition:
template<class T>
struct IsIntFloatOrString {
enum { value = boost::is_same<T, int>::value
or boost::is_same<T, float>::value
or boost::is_same<T, string>::value };
};
Use boost::enable_if and disable_if:
template<typename T1>
typename boost::enable_if<IsIntFloatOrString<T1> >::type
func(T1 &t) {
cout << "t1" << endl;
}
template<typename T2>
typename boost::disable_if<IsIntFloatOrString<T2> >::type
func(T2 &t) {
cout << "t2" << endl;
}
You cannot partially specialise functions in C++.
Perhaps this is not the terminology you mean. You can use templates like boost::is_same<T1, T2> to perform conditional logic based on the given template parameter. You can also use T in any place where you'd use any other type, such as in typeid(T).name():
template <typename T>
void foo(T&) {
if (boost::is_same<T, int>::value)
std::cout << "int lol";
else
std::cout << typeid(T).name();
}
(Although I'd not recommend using typeid().name() as its value is not specified by the standard and can vary from the type written in your code, to a mangled symbol, or the lyrics to Pokerface.)
Addendum Like other answerers, I would personally choose template specialisation itself or just plain ol' function overloading. I don't know why you're averse to them, but that is what they are there for.
As Tomalak already said in his answer you can not partially specialize a template function, but if you change your function to be a static member function in a template class, you could do it.
However, a better approach would be function overloading.
This is how to make it work without ugly syntax a and !b and !c for enable_if in case of arbitrary number of conditions.
If we know that partial specialization don't work work function but work with classes, let's use classes! We should hide it from people, but we can use them!
OK, code:
#include <type_traits>
#include <iostream>
template <typename T>
class is_int_or_float : public std::integral_constant<bool, std::is_same<T, int>::value || std::is_same<T, float>::value> {
};
template<typename T, typename Enable = void> //(2)
struct Helper {
static void go(const T&) {
std::cout << "all"<< std::endl;
}
};
template<typename T>
struct Helper<T, typename std::enable_if<is_int_or_float<T>::value>::type> { // (3)
static void go(const T&) {
std::cout << "int or float" << std::endl;
}
};
template<typename T>
struct Helper<T, typename std::enable_if<std::is_pointer<T>::value>::type> { // (3)
static void go(const T&) {
std::cout << "pointer" << std::endl;
}
};
template<typename T>
void func(const T& arg) {
Helper<T>::go(arg); // (1)
}
int main() {
char c;
int i;
float f;
int* p;
func(c);
func(i);
func(f);
func(p);
}
(1) First of all just for every type call helper. No specialization for functions.
(2) Here we add one dummy argument. We don't have to specify it on calling because it's default to void
(3) In 3 we just give void, when we allow T and anything else (or SFINAE as in our case). One important thing is that we shouldn't allow some T twice or more.
Notes:
We can also change default type to std::true_type, after that we will be able to get rid of std::enable_if (std::enable_if<some_trait<T>::value> will be change to just some_trait<T>::type). I'm not sure which
This code uses type traits from C++11. If you don't have c++11 support you may write your own traits or use type traits from boost
Live example