暧昧的模板怪异

kei*_*eis 8 c++ gcc templates specialization

我有以下代码(抱歉大代码块,但我不能再缩小它)

template <bool B>
struct enable_if_c {
      typedef void type;
};

template <>
struct enable_if_c<false> {};

template <class Cond>
struct enable_if : public enable_if_c<Cond::value> {};

template <typename X>
struct Base { enum { value = 1 }; };

template <typename X, typename Y=Base<X>, typename Z=void>
struct Foo;

template <typename X>
struct Foo<X, Base<X>, void> { enum { value = 0 }; };

template <typename X, typename Y>
struct Foo<X, Y, typename enable_if<Y>::type > { enum { value = 1 }; };

int main(int, char**) {
        Foo<int> foo;
}
Run Code Online (Sandbox Code Playgroud)

但它无法用gcc(v4.3)编译

foo.cc: In function ‘int main(int, char**)’:
foo.cc:33: error: ambiguous class template instantiation for ‘struct Foo<int, Base<int>, void>’
foo.cc:24: error: candidates are: struct Foo<X, Base<X>, void>
foo.cc:27: error:                 struct Foo<X, Y, typename enable_if<Y>::type>
foo.cc:33: error: aggregate ‘Foo<int, Base<int>, void> foo’ has incomplete type and cannot be defined
Run Code Online (Sandbox Code Playgroud)

好的,所以这是模棱两可的.但我并不认为这是一个问题,因为在使用专业化时,它几乎总是会有些含糊不清.但是,只有在使用类时才会触发此错误enable_if<...>,如果我将其替换为类似以下的类则没有问题.

template <typename X, typename Y>
struct Foo<X, Y, void > { enum { value = 2 }; };
Run Code Online (Sandbox Code Playgroud)

为什么这个课程不会引起歧义,而其他人呢?具有true :: value的类不是两个相同的东西吗?无论如何,任何关于我做错的提示都会受到赞赏.

感谢您的答案,我的真正的问题(让编译器来选择我的第一个特)被替换解决struct Foo<X, Base<X>, void>struct Foo<X, Base<X>, typename enable_if< Base<X> >::type >这似乎工作就是我想要的.

ava*_*kar 12

你的问题的要点是你有:

template <typename X, typename Y, typename Z>
struct Foo {};

template <typename X>
struct Foo<X, Base<X>, void> {};                   // #1

template <typename X, typename Y>
struct Foo<X, Y, typename whatever<Y>::type> {};   // #2
Run Code Online (Sandbox Code Playgroud)

而你正试图将它与之匹配

Foo<int, Base<int>, void>
Run Code Online (Sandbox Code Playgroud)

显然,两个专业都匹配(第一个X = int,第二个X = int, Y = Base<int>).

根据标准,第14.5.4节,如果有更多匹配的特化,则构造它们之间的部分排序(如14.5.5.2中所定义),并使用最专业的排序.然而,在你的情况下,没有一个比另一个更专业.(简单地说,模板比另一种更专业的,如果你能代替一些类型和结果得到前者的签名后者模板的每个类型参数.另外,如果你有whatever<Y>::type,你替换YBase<X>whatever<Base<X> >::type没有void,即没有处理.)

如果您要更换#2

template <typename X, typename Y>
struct Foo<X, Y, void > {};                        // #3
Run Code Online (Sandbox Code Playgroud)

然后候选集再次包含两个模板,然而,#1比#3更专业,因此被选中.

  • @keis:问题是专业化是一个偏序,在某些情况下,两个元素实际上没有被排序.现在,如果你陈述了你真正想要实现的目标(编写一个模板,当用int实例化时的值为1,而当用X实例化时,它的值为2)那么你可能会得到更好的提示 (2认同)