6

我喜欢在模板类中使用本地类来执行诸如“静态 if”之类的构造。但是我遇到了 gcc 4.8 不想编译我的代码的问题。但是 4.7 可以。

这个样本:

#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();
}

选项:

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

任务:

  1. 给定具有不同打印接口的 A 和 B 类。
  2. 编写一个可以打印 A 和 B 的通用类 Test。
  3. 不要污染任何命名空间或类范围。

代码说明:

  1. 这只是一个干净的例子。
  2. 我使用这样的方法,因为我想概括构造“静态 if”。看,我通过它们的字段将参数传递给 IfA 和 IfB 类,而不是直接传递给 print() 函数。
  3. 我经常使用这种结构。
  4. 我发现这些结构不应该在(污染)类范围内。我的意思是它们应该放在方法范围内。

所以这个问题。

此代码不能用 GCC 4.8 编译。因为它会检查所有类,即使它们从未使用过。但它没有用二进制实例化它们(我已经注释了导致错误的行并用 gcc 4.8 编译它)。证明:

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

看,没有 Test::print()::IfB::print()。(见后文:'void Test::print()::IfB::print() [with T = A]')

如果我使用 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();
                 ^
  1. 它是 GCC 4.8 错误吗?
  2. 还是 GCC 4.7 错误?也许代码不应该被编译。
  3. 或者这是我的错误,我不应该依赖编译器行为/不应该使用这种方法来实现“静态 if”。

附加信息:

这个简单的代码可以在 4.7 上编译,但不能在 4.8 上编译。我把它缩短了。

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();
}

错误:

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();
                 ^

已经测试了两个 GCC 4.8 版本:2012.10 和 2013.02。希望这是 GCC 4.8 的错误并且可以修复。

4

2 回答 2

3

LocalClass不是模板。“不使用则不实例化”规则仅适用于类模板的成员函数。

也就是说,当Test::print()被实例化时,里面的所有东西都会被激活,包括其本地类中未使用的成员。

于 2013-03-17T04:28:04.480 回答
3

您的代码中没有 SFINAE。

SFINAE 适用于模板参数推导和参数替换(SFINAE 中的“S”代表替换),但程序中唯一的替换发生AT模板参数列表中的替换时Test,这不会失败。

然后调用print()which instantiates Test<A>::print(),它不涉及任何替换,并且由于value.notExist();无效而出现错误。

SFINAE 必须用于替换上下文中,例如由函数调用导致的模板参数推导或使用默认参数推导模板参数时。

于 2013-03-17T16:56:47.560 回答