【问题标题】:Templates with same signature not causing a compiler error具有相同签名的模板不会导致编译器错误
【发布时间】:2013-02-15 09:14:12
【问题描述】:

以下程序在两个单独的命名空间(AB)中定义了两个函数模板,A::foo<>()B::foo<>()。这两个函数模板签名相同,仅在分配给它们的第二个模板参数的默认参数上有所不同。最终,它们的名字通过一对对应的using 声明进入main() 的范围:

#include <type_traits>

namespace A
{
    template<
        typename T,
        typename = typename std::enable_if<
            std::is_same<T, int>::value        // Let this be condition C
            >::type
        >
    void foo(T) { }
}

namespace B
{
    template<
        typename T,
        typename = typename std::enable_if<
            !std::is_same<T, int>::value       // This is the negation of C
            >::type
        >
    void foo(T) { }
}

int main() {
    using A::foo;
    using B::foo; // COMPILES: Is this legal?

    foo(42); // Invokes A::foo(), non-ambiguous because of SFINAE
}

我希望第二个using 声明会在此处导致编译错误:毕竟,当我尝试在同一个命名空间中定义这两个模板时,我会得到这样的结果。

令我惊讶的是,我尝试过的每个编译器(GCC 4.7.2、GCC 4.8.0 beta、ICC 13.0.1、Clang 3.2)都会编译程序并调用A::foo()

问题 #1:这是正确的吗?这可能是“不需要诊断”的情况吗?鼓励参考 C++11 标准。


现在考虑上述程序的这种变体,它基本上使用类而不是命名空间来实现相同的效果:

#include <type_traits>

struct X
{
    template<
        typename T,
        typename = typename std::enable_if<
            std::is_same<T, int>::value        // Here is condition C again
            >::type
        >
    static void foo(T) { }
};

struct Y
{
    template<
        typename T,
        typename = typename std::enable_if<
            !std::is_same<T, int>::value       // And the negation of C again
            >::type
        >
    static void foo(T) { }
};

struct Z : X, Y
{
   using X::foo;
   using Y::foo; // COMPILES: Is this legal?
};

int main() {
    Z::foo(42); // Invokes X::foo(), non-ambiguous because of SFINAE
}

该程序也可以在上述所有编译器上编译,而我预计第二个 using 声明会导致编译器错误。

问题 #2:这是正确的吗?这可能是“不需要诊断”的情况吗?鼓励参考 C++11 标准。

【问题讨论】:

    标签: c++ templates c++11 using-declaration


    【解决方案1】:

    对于问题 #1:这似乎是允许的,因为没有禁止它的规则。如果 using 声明引入的函数与直接在 using 声明引入名称的命名空间中声明的函数冲突,C++11 标准在注释中提到了这一点,在规则旁边禁止这样做。

    它在 §7.3.3[namespace.udecl]/14 中说:

    如果命名空间范围或块范围内的函数声明具有 与 a 引入的函数相同的名称和相同的参数类型 using-declaration,并且声明不声明相同 函数,程序格式错误。 [...]

    这是将某种冲突指定为无效的规范性文本。

    没有规范性文本说明两个 using 声明不会以相同的方式发生冲突,但请注意,两个 using 声明之间的类似冲突在声明时并非无效。同一段继续:

    [...] [注意:两个使用声明可能会引入函数 相同的名称和相同的参数类型。如果,打电话给不合格的 函数名,函数重载决议选择函数 由这样的 using 声明引入,函数调用是 格式不正确。 [ 例子:

     namespace B { 
       void f(int); 
       void f(double); 
     }
     namespace C { 
       void f(int);
       void f(double); 
       void f(char);
     } 
    
     void h() {
       using B::f;    // B::f(int) and B::f(double)
       using C::f;    // C::f(int), C::f(double), and C::f(char)
       f(’h’);        // calls C::f(char)
       f(1);          // error: ambiguous: B::f(int) or C::f(int)?
       void f(int);   // f(int) conflicts with C::f(int) and B::f(int)
     }
    

    —结束示例]—结束注释]

    对于问题 #2,描述类成员声明和类级别 using 声明之间冲突情况的类似规范性文本在以下段落中,§7.3.3/15

    当使用声明将名称从基类带入派生类时 类范围、成员函数和成员函数模板 派生类覆盖和/或隐藏成员函数和成员 具有相同名称的函数模板,参数类型列表(8.3.5), 基类中的 cv-qualification 和 ref-qualifier(如果有)(而不是 比冲突)。 [ 注意:对于命名为 a 的 using 声明 构造函数,见 12.9。 ——尾注]

    同样没有关于 using 声明之间的冲突的文本,但类似于前面案例中的注释,有两个可能指定冲突函数的 using 声明不是格式错误的,因为没有禁止这些声明共存的文本.和前面的情况一样:如果对于函数调用,重载决策选择了这两个函数,则调用是格式错误的。

    在您的示例中,SFINAE 将始终从重载集中消除可能发生冲突的函数之一,因此在这两种情况下都不存在问题。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多