为什么 M::operator<< 导致 link 错误而不是 std::cout::operator<<

Why M::operator<< causes a link error instead of std::cout::operator<<

问题是,为什么只有对 M::operator<< 的调用会导致 link 错误,而不是对 std::cout::operator<< 的调用?

代码如下:

#include <iostream>

struct  M {
        inline M() {}

        template <typename T>
        inline M& operator <<(const T& val) {
            std::cout << "ref." << val;
            return *this;
        }

        template <typename T>
        inline M& operator <<(T* const& pointer) {  // NOLINT
            std::cout << "ptr." << pointer;
            return *this;
        }
};

class PJTest
{
public:
    ~PJTest()
    {
        M()
            << "Failed to remove file '" << fname << "' because: stuff\n"; // 25

        std::cout
            << "Failed to remove file '" << fname << "' because: stuff\n"; // 28
    }

protected:
    static auto constexpr fname = "what's in a name?";
};

int main() {
    PJTest pt;
}

g++ -g -O0 -std=c++11 -Wall -pedantic -Wextra wtf.cc 编译结果

wtf_test.cc:25: undefined reference to `PJTest::fname'

请注意第 28 行没有错误!

g++ -g -O2 -std=c++11 -Wall -pedantic -Wextra wtf.cc 成功。 (来自 Ubuntu 14.04LTS 的 g++ 4.8.4)并且行为与 G++ 5.3.0

相同

无论优化级别如何,使用 clang++ 编译总是失败,但同样,仅针对第 25 行;我知道我可以通过添加 constexpr const char* PJTest::fname; 来解决这个问题,但我想了解 为什么它会导致 clang++.

出错

答案是 - 因为 std::ostream 有选择的 const char* 的非模板版本:

你的程序中也有这样的版本:

    inline M& operator <<(const char* val) {
        std::cout << "str." << val;
        return *this;
    }

您的代码编译 w/o 问题。

更多背景 - 你真正的 fname 类型是 char[18] - 所以编译器的最佳猜测是:

    template <typename T>
    inline M& operator <<(const T& val) {
        std::cout << "ref." << val;
        return *this;
    }

如您所见 - 此版本中需要引用 - 或多或少这意味着 fname 应该有一个地址 - 它不可能是 true optimized-out const。

你也可以通过定义它来给这个变量一个地址 - 就像 class:

的任何其他静态变量一样
class PJTest
{
//....
protected:
    static auto constexpr fname = "what's in a name?";
};
decltype(PJTest::fname) constexpr PJTest::fname;

过载限制是一个非常棘手的课题 - 大多数细节都是 here, with template as new complication level - read here

只是为了让事情更简单一些 - 让我们研究更简单的形式:

  1. 无法 link - 因为选择了 f(int const&) - 它需要 "address"

代码:

class PJTest
{
public:
    static auto constexpr fvalue = 113;
};
//decltype(PJTest::fname) constexpr PJTest::fname;

void f(const int&) {}

void f(double) {}


int main() {
    f(PJTest::fvalue);
}
  1. 没问题 - const int 转换为 const double - 不需要 "address":

代码:

class PJTest
{
public:
    static auto constexpr fvalue = 113;
};
//decltype(PJTest::fname) constexpr PJTest::fname;

void f(double) {}

int main() {
    f(PJTest::fvalue);
}
  1. 编译良好 - 因为选择了 non-template 版本 - non-template 版本总是作为首选匹配(这或多或少是 std::ostream 的情况 - 我的建议是如何改变你的 "stream" class):

代码:

class PJTest
{
public:
    static auto constexpr fvalue = 113;
};
//decltype(PJTest::fname) constexpr PJTest::fname;

template <typaname T>
void f(const T&) {}

void f(double) {}


int main() {
    f(PJTest::fvalue);
}
  1. 未能达到 link - 因为我们只有模板版本 - 它需要 "address" - 这相当于你的问题版本:

代码:

class PJTest
{
public:
    static auto constexpr fvalue = 113;
};
//decltype(PJTest::fname) constexpr PJTest::fname;

template <typaname T>
void f(const T&) {}

int main() {
    f(PJTest::fvalue);
}