Supposedly members of a template class shouldn't be instantiated unless they are used.
However this sample seems to instantiate the do_something member and the enable_if fails (which would be expected if we'd instantiated it - but AFAIK we did not).
Am I missing something really basic here?
#include <string>
#include <boost/utility.hpp>
struct some_policy {
typedef boost::integral_constant<bool, false> condition;
};
struct other_policy {
typedef boost::integral_constant<bool, true> condition;
};
template <typename policy>
class test {
void do_something(typename boost::enable_if<typename policy::condition>::type* = 0) {}
};
int main() {
test<other_policy> p1;
test<some_policy> p2;
}
coliru
From C++11 14.7.1/1:
The implicit instantiation of a class template specialization causes the implicit
instantiation of the declarations, but not of the definitions or default arguments, of the class member functions
So the function declaration is instantiated; that fails since it depends on an invalid type.
(Unfortunately, I don't have any historic versions of the standard to hand, but I imagine this rule was similar in C++98)
Mike Seymour already answered why it doesn't work, here's how to workaround it:
#include <string>
#include <boost/utility.hpp>
struct some_policy {
typedef boost::integral_constant<bool, false> condition;
};
struct other_policy {
typedef boost::integral_constant<bool, true> condition;
};
template <typename policy>
class test {
private:
template<typename P>
void do_something_impl(typename boost::enable_if<typename P::condition>::type* = 0) {}
public:
void do_something()
{
do_something_impl<policy>();
}
};
int main() {
test<other_policy> p1;
test<some_policy> p2;
}
Quick rule of thumb: If you want to do SFINAE, you need a member function template.
SFINAE happens only on template function/method (here, it is your class which is template),
You may do in C++11 (default template parameter for function/method):
template <typename policy>
class test {
template <typename T = policy>
void do_something(typename boost::enable_if<typename T::condition>::type* = 0) {}
};
You may alternatively use specialization, something like
template <bool> struct helper_do_something {};
template <> struct helper_do_something<true>
{
void do_something() { /* Your implementation */ }
};
template <typename policy>
class test : helper_do_something<T::condition::value>
{
// do_something is inherited (and it is present only when T::condition::value is true)
};
Related
Inside a specialization for a templated class I call a function which should not compile, but in my mind it shouldn't matter as I pass false as a template argument and the compiler shouldn't instantiate that class in the first place:
#include <iostream>
template <typename T>
struct outerstruct
{
template <bool b>
struct innerStruct
{
};
template<>
struct innerStruct<true>
{
using type = std::underlying_type_t<T>;
};
template<>
struct innerStruct<false>
{
};
innerStruct<false> member; // std::underlying_type_t cannot be called on a T,
// even though that class shouldn't be instantiated
// because I'm passing false (or I think)
};
int main()
{
outerstruct<int> a;
}
Why is innerStruct<true> being instantiated and the compiler trying to call underlying_type if I pass the false argument?
Very confusingly for me, I can make it behave the way I want (ie., have the compiler ignore the call to std::underlying_type_t) by just adding another template argument to innerClass, here I add a U:
#include <iostream>
template <typename T>
struct outerstruct
{
template <typename ExtraArg, bool b>
struct innerStruct
{
};
template<typename ExtraArg>
struct innerStruct<ExtraArg, true>
{
using type = std::underlying_type_t<T>;
};
template<typename ExtraArg>
struct innerStruct<ExtraArg, false>
{
};
//innerStruct<bValue> member;
innerStruct<int, true> member; // Now it's working as I expected,
// I can pass int or whatever argument to ExtraArg
// and it compiles as long as I have 'false'
};
int main()
{
outerstruct<int> a;
}
What is the difference between the first and the second examples? What business does template specialization innerStruct<true> have being instantiated if in the first example I only called innerStruct<false>
I would like to perform a typedef, if and only if a compile-time condition is met. If the condition is not met, no typedef shall be performed at all.
Is that possible in C++11?
Example:
class A {
std::conditional_typedef<true,int,myType1>; // Performs "typedef int myType1".
std::conditional_typedef<false,int,myType2>; // Does nothing at all.
};
I am looking for this fictional std::conditional_typedef.
Another way can be passing from the specialization of a base class
// foo is a light struct (only a typedef or not at all) that can be
// developed in two versions
template <bool>
struct foo;
template <>
struct foo<true>
{ typedef int myType1; }; // or using myType1 = int;
template <>
struct foo<false>
{ };
template <bool B>
struct bar : public foo<B> // B can be a type_traits value derived
// from template arguments for bar
{
// potential complex struct or class, developed only one time
};
int main()
{
bar<true>::myType1 mt1 { 1 };
// bar<false>::myType1 mt2 { 1 }; error: ‘myType1’ is not a member of ‘bar<false>’
}
Unfortunately desired syntax can't be possible as names that are passed to the template instantiation have to be already defined. In your case myType1 and myType2 wouldn't name anything from compiler point of view. However if you're not insisting on the syntax you've mentioned you could try to use std::enable_if as follows:
#include <type_traits>
struct A {
struct myType1: std::enable_if<true, int> { }; //std::conditional_typedef<true,int,myType1>; // Performs "typedef int myType1".
struct myType2: std::enable_if<false, int> { }; //std::conditional_typedef<false,int,myType2>; // Does nothing at all.
};
int main() {
A::myType1::type i;
//A::myType2::type i2; // causes error: no type named 'type' in 'A::myType2'
(void)i;
}
[live demo]
Edit:
Yet another way that came to my mind (utilizing using with default template parameter):
#include <type_traits>
struct A {
template <class T = int>
using myType1 = typename std::enable_if<true, T>::type;
template <class T = int>
using myType2 = typename std::enable_if<false, T>::type;
};
int main() {
A::myType1<> i;
//A::myType2<> j;
(void)i;
}
[live demo]
Similar to std::enable_if, you could have your class descend from a template that does your typedef if you want to use your own names and not have to do A::mytype1::type. The downside is that you'd have to descend from multiple structs if you want to do this a lot.
namespace impl {
template<bool, typename> struct A;
template<typename T> struct A<true, T>{ typedef T mytype1; };
template<typename T> struct A<false, T> {};
}
struct A : public impl::A<condition, int> {
//If condition is met, then ::mytype1 will be defined as a typedef int
};
int main() {
A::mytype1 i; //Will fail if condition wasn't met
}
Is it possible to do something like the following that compiles without template specialization?
template <class T>
class A {
public:
#if std::is_same<T, int>
void has_int() { }
#elif std::is_same<T, char>
void has_char() { }
#endif
};
A<int> a; a.has_int();
A<char> b; b.has_char();
Yes. Make the function templates and then conditionaly enable them using std::enable_if:
#include <type_traits>
template <class T>
class A {
public:
template<typename U = T>
typename std::enable_if<std::is_same<U,int>::value>::type
has_int() {}
template<typename U = T>
typename std::enable_if<std::is_same<U,char>::value>::type
has_char() {}
};
int main()
{
A<int> a;
a.has_int(); // OK
// a.has_char(); // error
}
The solution from the other answer might not be feasible if the class is big and has got many functions that need to regardless of T. But you can solve this by inheriting from another class that is used only for these special methods. Then, you can specialize that base class only.
In C++14, there are convenient type aliases so the syntax can become:
std::enable_if_t<std::is_same<U, int>::value>
And C++17, even shorter:
std::enable_if_t<std::is_same_v<U, int>>
Yes, with template specialization :
template <class T>
class A;
template <>
class A<int>
{
void had_int(){}
};
template <>
class A<char>
{
void had_char(){}
};
My question is w.r.t the following thread : specialize a member template without specializing its parent
I'm absolutely fine with the standard saying that it is illegal to do so. But i want to understand why is it illegal to do so? What would be impact had it been allowed?
Maybe because of something like this:
template <typename T>
struct foo
{
template <typename U>
struct bar
{
typedef U type;
};
};
template <typename T>
struct foo<T>::bar<int> // imaginary
{
typedef void type;
};
template <>
struct foo<float>
{
template <typename U>
struct bar
{
typedef U* type;
};
};
// is it void [foo<T>::bar<int>] or
// int* [foo<float>::bar<U>]?
typedef foo<float>::bar<int>::type ambiguous;
A sensible solution is to say "we'll make the entire thing explicit".
I have code which works in VC9 (Microsoft Visual C++ 2008 SP1) but not in GCC 4.2 (on Mac):
struct tag {};
template< typename T >
struct C
{
template< typename Tag >
void f( T ); // declaration only
template<>
inline void f< tag >( T ) {} // ERROR: explicit specialization in
}; // non-namespace scope 'structC<T>'
I understand that GCC would like me to move my explicit specialization outside the class but I can't figure out the syntax. Any ideas?
// the following is not correct syntax, what is?
template< typename T >
template<>
inline void C< T >::f< tag >( T ) {}
You can't specialize a member function without explicitly specializing the containing class.
What you can do however is forward calls to a member function of a partially specialized type:
template<class T, class Tag>
struct helper {
static void f(T);
};
template<class T>
struct helper<T, tag1> {
static void f(T) {}
};
template<class T>
struct C {
// ...
template<class Tag>
void foo(T t) {
helper<T, Tag>::f(t);
}
};
GCC is in the clear, here. MSVC has a non-standard extension that allows in-class specialization. The standard, however, says:
14.7.3.2:
2. An explicit specialization shall be declared in the namespace of
which the template is a member, or, for member templates, in the
namespace of which the enclosing class or enclosing class template is
a member. An explicit specialization of a member function, member
class or static data member of a class template shall be declared in
the namespace of which the class template is a member.
Additionally, you can't partially specialize a function. (Though I'm unsure about the details in your case, that would be the final blow.)
You could do this:
#include <iostream>
struct true_type {};
struct false_type {};
template <typename T, typename U>
struct is_same : false_type
{
static const bool value = false;
};
template <typename T>
struct is_same<T, T> : true_type
{
static const bool value = true;
};
struct tag1 {};
struct tag2 {};
template< typename T >
struct C
{
typedef T t_type;
template< typename Tag >
void foo( t_type pX)
{
foo_detail( pX, is_same<Tag, tag1>() );
}
private:
void foo_detail( t_type, const true_type& )
{
std::cout << "In tag1 version." << std::endl;
}
void foo_detail( t_type, const false_type& )
{
std::cout << "In not tag1 version." << std::endl;
}
};
int main(void)
{
C<int> c;
c.foo<tag1>(int());
c.foo<tag2>(int());
c.foo<double>(int());
}
Though this is somewhat ugly.
Came across this question. This should work:
struct tag {};
template< typename T >
struct C {
template< typename Tag, typename std::enable_if<std::is_same<Tag, tag>::value, int>::type = 0>
void f( T ){
std::cout<<"tag type" <<std::endl;
}
template< typename Tag, typename std::enable_if<!std::is_same<Tag, tag>::value, int>::type = 0>
void f( T ){
std::cout<<"non tag type" <<std::endl;
}
};
I know this may not satisfy you, but I do not believe you may not have a specialization enclosed within a non-explicitly-specialized structure.
template<>
template<>
inline void C< tag1 >::foo< tag2 >( t_type ) {}
The basic detail is that you need to put the code declaration outside of the class so that there is only one declaration of it. If you leave it in a header, declared for all including c++ source files to see, you end up with multiple instances of the same class defined. Just put the declaration of the templated function in the header file, and then move the declared specializations of that templated function into your C++ source file and all will be good because the compiler will generate the correct references based on the types of specialization you use in your source code.
For example you want to create an extensible Number class like java's Number class so that you can pass numeric values around. If this is in the .h/.hpp file, the compiler will know how to generate references to each specialization because the return type is part of the generated function name that the compiler generates references for.
class Number {
Int32 intVal;
double d;
float f;
Int64 longVal;
std::string strVal;
public:
template<T>
T getValue();
... other functions needed go here...
};
In your C++ source file you can just write the following.
template<>
Int32 Number::getValue() { return intVal; }
template<>
double Number::getValue() { return d; }
template<>
float Number::getValue() { return f; }
template<>
Int64 Number::getValue() { return longVal; }
template<>
std::string Number::getValue() { return strVal; }
Now when you pass a Number around, depending on which value type you assign it to, you can use an appropriate value type on a getValue<>() calls.
From https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85282 (Patrick Palka)
[...] as a workaround, instead of e.g.:
struct A {
template<class T> struct B;
template<>
struct B<int> { }; // unsupported class-scope explicit specialization
};
in C++20 one can do:
struct A {
template<class T>
struct B;
template<std::same_as<int> T>
struct B<T> { };
};
or in C++17:
struct A {
template<class T, class = void>
struct B;
template<class T>
struct B<T, std::enable_if_t<std::is_same_v<int,T>>> { };
};
While the code is perhaps non-compliant, one practical solution is to switch to clang, where it works fine.
https://godbolt.org/z/hPbP1M
gcc doesn't allow member function full specialization inside a class.
This is because functionality wise it is the same as function overloading.
So just remove template<> from specializations and make them function overloads instead.
Try this:
template <> template<typename T> inline void C<T> :: foo<tag2>(T) {}