为什么 M::operator<< 会导致链接错误,而不是 std::cout::operator<<
Why M::operator<< causes a link error instead of std::cout::operator<<
问题是,为什么只有对M::operator<<
的调用会导致链接错误,而不是在应该调用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: stuffn"; // 25
std::cout
<< "Failed to remove file '" << fname << "' because: stuffn"; // 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;
}
您的代码编译没有问题。
更多背景 - 您的真实fname
类型是char[18]
- 因此编译器的最佳猜测是:
template <typename T>
inline M& operator <<(const T& val) {
std::cout << "ref." << val;
return *this;
}
如您所见 - 此版本中需要引用 - 或多或少这意味着fname
应该有一个地址 - 它不可能是真正的优化常量。
你也可以通过定义这个变量来给它一个地址 - 就像类的任何其他静态变量一样:
class PJTest
{
//....
protected:
static auto constexpr fname = "what's in a name?";
};
decltype(PJTest::fname) constexpr PJTest::fname;
过载绝缘是一个非常棘手的主题 - 大部分细节都在这里,模板作为新的复杂程度 - 阅读这里。
只是为了让事情更简单一点 - 让我们研究更简单的形式:
- 无法链接 - 因为选择了
f(int const&)
- 它需要"地址"
法典:
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);
}
- 一切都很好 -
const int
转换为const double
- 不需要"地址":
法典:
class PJTest
{
public:
static auto constexpr fvalue = 113;
};
//decltype(PJTest::fname) constexpr PJTest::fname;
void f(double) {}
int main() {
f(PJTest::fvalue);
}
- 编译很好 - 因为选择了非模板版本 -
- 非模板版本始终匹配为首选(这或多或少是 std::ostream 的情况 - 以及我如何更改"流"类的建议):
法典:
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);
}
- 无法链接 - 因为我们只有模板版本 - 并且它需要"地址" - 这相当于您的问题版本:
法典:
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);
}
相关文章:
- 请解释这句话(cout<<1+int((a<b)^((b-a)&1) )<<endl
- 呼叫运营商<<临时
- 如何防止clang格式在流运算符调用之间添加换行符<<
- 为什么 std::optional::operator=(U&&) 要求你是非标量类型?
- 'operator='已弃用:改用 QDir::setPath()
- <<操作员在下面的行中工作
- 过载'operator new'如何导致无限循环?
- 与'operator='不匹配(操作数类型'String'且"void")
- SegFault 同时使用 std::string::operator+= 和函数作为参数
- 处理"no operator found"
- 如何编写 operator= 用于使用虚拟方法与非平凡成员的匿名联合
- 运算符重载:"operator+"必须采用零个或一个参数
- 使用 operator() 扩展 Eigen::EigenBase
- 重载operator< & lt;作为会员打印
- Operator< & lt;重载隐藏其他
- 重载ostream&时转换错误无效;operator< & lt;
- std::ostream&operator< & lt; (std:: ostream&压力,压力& &;val)
- operator< & lt;在c++中为类中的类重载
- 将std::endl传递给std::operator<<
- operator< & lt;重载、名称空间和模板