【问题标题】:Is this valid C++ 11这是有效的 C++ 11
【发布时间】:2016-01-13 19:30:50
【问题描述】:

我有以下代码可以在 g++ 下编译,但不能使用 clang。

如果以各种细微的方式更改代码,例如合并 2 个命名空间声明,Clang 将编译代码。

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

使用 clang 编译:

clang -fsyntax-only -std=c++11 testcase.cpp

使用 g++ 编译:

g++ -fsyntax-only -std=c++11 testcase.cpp

版本是 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.

【问题讨论】:

  • 友元函数(即使在类中声明)具有命名空间范围。所以在这种情况下,你有 2 个bool Root::g() 的函数声明。模板参数不会更改声明。这就是为什么您的微小更改使此代码起作用的原因。实际上我更惊讶的是它可以用 g++ 编译。
  • @SimonKraemer 您可以多次声明函数。并且friend 声明无论如何都不会重新声明该函数。
  • 这是您的完整代码还是只是相关部分?
  • @Barry 你是对的。我的错。我试图用 MSVC 编译它并进入链接器错误。所以我定义了两个 g() 函数..... -.-
  • @SimonKraemer,这是原始问题的大幅缩减版本。最初是断言库与 int 库交互的问题(为了停止不安全的转换 - 我做了很多小事)。

标签: c++ c++11 clang language-lawyer


【解决方案1】:

我相信这是一个 clang 错误。从 [temp.param],我们有:

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

可供使用的默认模板参数集是通过合并来自的默认参数获得的 模板的所有先前声明都以相同的方式默认函数参数是(8.3.6)。

后一点表示我们可以写:

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

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

h<int>();

这是由 clang 编译的格式完美的代码。我们不能根据引用的规则将默认模板参数指定为g,因为g 之前已声明,但指定它仍应保留Defaulted可通过合并步骤用作void。如果默认参数可用,那么查找应该能够找到我们想要的g

一种解决方法是简单地为我们关心的专业加好友:

friend bool g<>(MyClass value);

【讨论】:

  • 谢谢 - 我同意你所说的合并,这对我来说很有意义。但是我认为您根本不允许在朋友 decl 中指定模板参数的默认值。鉴于此,我对第一段的(错误)解释是:通过使用已经具有默认值(在其他地方声明)的模板参数声明朋友,我必须在朋友声明旁边提供函数体?
  • 想了想,现在明白了。您可以在朋友模板上指定默认值,我没有意识到,这让我很困惑。所以这看起来很像一个clang bug。
猜你喜欢
  • 1970-01-01
  • 2011-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-02
  • 1970-01-01
  • 2011-05-23
  • 1970-01-01
相关资源
最近更新 更多