2

以下程序在两个单独的命名空间 (和)中定义了两个函数模板A::foo<>()和。这两个函数模板的签名相同,只是分配给它们的第二个模板参数的默认参数不同。最终,它们的名称被相应的一对声明带入了范围:B::foo<>()ABmain()using

#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 标准。

4

1 回答 1

4

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

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

如果命名空间范围或块范围内的函数声明与 using 声明引入的函数具有相同的名称和相同的参数类型,并且声明未声明相同的函数,则程序是非良构的。[...]

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

没有规范性文本表明两个 using 声明不会以相同的方式发生冲突,但请注意,两个 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中:

当 using-declaration 将基类中的名称带入派生类范围时,派生类中的成员函数和成员函数模板会覆盖和/或隐藏具有相同名称的成员函数和成员函数模板,parameter-type-list (8.3 .5)、cv-qualification 和 ref-qualifier(如果有)在基类中(而不是冲突)。[ 注意:对于命名构造函数的 using 声明,请参见 12.9。——尾注]

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

在您的示例中,SFINAE 将始终从重载集中消除一个潜在的冲突函数,因此在这两种情况下都没有问题。

于 2013-03-01T00:48:48.533 回答