C ++ GCC Why can this sfinae code be compiled with GCC 4.7, but not with 4.8?

I like to use local classes in template classes to execute constructs such as "static if". But I ran into a problem that gcc 4.8 does not want to compile my code. However 4.7 does.

This example:

#include <type_traits> #include <iostream> #include <string> using namespace std; struct A { void printA() { cout << "I am A" << endl; } }; struct B { void printB() { cout << "I am B" << endl; } }; template <typename T> struct Test { void print() { struct IfA { constexpr IfA(T &value) : value(value) { } T &value; void print() { value.printA(); } }; struct IfB { constexpr IfB(T &value) : value(value) { } T &value; void print() { value.printB(); } }; struct Else { constexpr Else(...) {} void print() { } }; typename conditional<is_same<T, A>::value, IfA, Else>::type(value).print(); typename conditional<is_same<T, B>::value, IfB, Else>::type(value).print(); } T value; }; int main() { Test<A>().print(); Test<B>().print(); } 

Options:

 g++ --std=c++11 main.cc -o local-sfinae 

Task:

  • For classes A and B with different print interfaces. A.
  • Write a general class test that can print both A and B.
  • Contaminate neither the namespace nor the content area.

Code Description:

  • This is just a pure example.
  • I use this approach because I want to generalize the construction of "static if". See That I pass arguments to the IfA and IfB classes through their fields, and not directly to the print () function.
  • I use such designs a lot.
  • I found that these constructions should not be in the (polluting) class. I mean, they should be placed in the scope of the method.

So the question is.

This code cannot be compiled with GCC 4.8. Because it checks ALL classes, even if they are never used. But it does not create them in binary format (I commented out the lines that cause errors, and compiled them with gcc 4.8). Evidence:

 $ nm local-sfinae |c++filt |grep "::If.*print" 0000000000400724 W Test<A>::print()::IfA::print() 00000000004007fe W Test<B>::print()::IfB::print() 

Look, there is no test :: print () :: IfB :: print (). (See Below: 'void Test :: print () :: IfB :: print () [with T = A]')

Errors if I compile the above code with gcc 4.8:

 g++ --std=c++11 main.cc -o local-sfinae main.cc: In instantiation of 'void Test<T>::print()::IfB::print() [with T = A]': main.cc:36:9: required from 'void Test<T>::print() [with T = A]' main.cc:49:21: required from here main.cc:34:17: error: 'struct A' has no member named 'printB' value.printB(); ^ main.cc: In instantiation of 'void Test<T>::print()::IfA::print() [with T = B]': main.cc:28:9: required from 'void Test<T>::print() [with T = B]' main.cc:50:21: required from here main.cc:26:17: error: 'struct B' has no member named 'printA' value.printA(); ^ 
  • Is this a GCC 4.8 bug?
  • Or is it a GCC 4.7 bug? Perhaps the code should not compile.
  • Or this is my mistake, and I should not rely on the behavior of the compiler / should not use this approach to implement "static if".

Additional Information:

This simple code compiles on 4.7, but not 4.8. I cut it.

 struct A { void exist() { } }; template <typename T> struct Test { void print() { struct LocalClass { constexpr LocalClass(T &value) : value(value) { } T &value; void print() { value.notExist(); } }; } T value; }; int main() { Test<A>().print(); } 

Errors:

 main.cc: In instantiation of 'void Test<T>::print()::LocalClass::print() [with T = A]': main.cc:16:9: required from 'void Test<T>::print() [with T = A]' main.cc:22:21: required from here main.cc:14:17: error: 'struct A' has no member named 'notExist' value.notExist(); ^ 

We tested two versions of GCC 4.8: 2012.10 and 2013.02. Hope this is GCC 4.8 error and it can be fixed.

+6
source share
2 answers

LocalClass not a template. The "not created if not used" rule applies only to member functions of class templates.

That is, when Test::print() is created, everything inside is animated, including an unused member of its local class.

+3
source

The code is missing SFINAE.

SFINAE is used when subtracting a template argument and substituting an argument ("S" in SFINAE stands for substitution), but the only substitution in your program occurs when replacing A with T in the list of Test template parameters, which does not fail.

Then you call print() , which creates an instance of Test<A>::print() , which does not contain any substitution, and you get an error because value.notExist(); not valid.

SFINAE should be used in substitution contexts, such as outputting a template argument caused by a function call, or when outputting template parameters with default arguments.

+3
source

All Articles