这是有效的C++ 11吗?

Sim*_*rne 24 c++ clang language-lawyer c++11

我有以下代码在g ++下编译,但不是用clang编译.

如果以各种次要方式进行更改,Clang将编译代码,例如合并2个名称空间声明.

// The problem disappears without namespaces.
namespace Root {
    // The problem disappears if 'g' is in the global namespace, and we change
    // the friend declaration to '::g'

    // The problem disappears if 'g' has void return type.

    // The problem disappears if we get rid of the 'Value' template argument
    // and the 'value' parameter.
    template<typename Value, typename Defaulted = void>
    bool g(Value value);

    // The problem disappears if MyClass is not a template.
    template<typename ClassValue>
    class MyClass {
    private:
        template<typename Value, typename Defaulted>
        friend bool g(Value value);
    };
}

// The problem disappears if we declare the Root namespace in a single block
// containing 'g', 'MyClass' and 'f'.

// The problem remains if we declare f in the global namespace and reference
// Root::g.
namespace Root {
    void f() {
        MyClass<int> value;

        g(value);
    }
}
Run Code Online (Sandbox Code Playgroud)

用clang编译:

clang -fsyntax-only -std=c++11 testcase.cpp
Run Code Online (Sandbox Code Playgroud)

要用g ++编译:

g++ -fsyntax-only -std=c++11 testcase.cpp
Run Code Online (Sandbox Code Playgroud)

版本是g ++ 4.9.2,clang 3.6.0,都在Ubuntu核心15.04上.

Clang给出了错误消息:

testcase.cpp:24:9: error: no matching function for call to 'g'
        g(value);
        ^
testcase.cpp:14:21: note: candidate template ignored: couldn't infer template argument 'Defaulted'
        friend bool g(Value value);
                ^
1 error generated.
Run Code Online (Sandbox Code Playgroud)

Bar*_*rry 7

我相信这是一个铿锵的错误.从[temp.param],我们有:

如果友元函数模板声明指定了默认模板参数,则该声明应该是一个定义,并且应该是翻译单元中函数模板的唯一声明.

可以使用的默认模板参数集是通过合并模板的所有先前声明中的默认参数获得的,其方式与默认函数参数相同(8.3.6).

后一点意味着我们可以写:

template <typename T, typename U=int>
void h();

template <typename T, typename U>
void h() { }

h<int>();
Run Code Online (Sandbox Code Playgroud)

这是一个完美的代码,可以编译.我们不能gg之前声明的那样根据引用的规则指定默认的template-argument ,但是指定它仍然Defaulted可以void通过合并步骤继续使用.如果default-argument可用,那么lookup应该能够找到g我们想要的.

解决方法是简单地告诉我们关心的专业化:

friend bool g<>(MyClass value);
Run Code Online (Sandbox Code Playgroud)