模板 operator= 与 const 参数的问题
issue with template operator= with const parameter
这是在 c++ 中,使用 Visual Studio 2019(没有尝试过其他编译器)。
我想添加一个模板化的 operator= 方法。如果参数是非常量,它工作正常。但是如果参数是常量,即使我制作一个带有常量参数的版本,它也不会被调用。相反,它做一个简单的浅拷贝。
如果我使用命名函数而不是运算符,它会按预期工作。同样,如果它不是模板化的,则按预期调用运算符。组合似乎是问题所在。
这是一个展示问题的示例。
class CTest
{
public:
int x{};
CTest() = default;
CTest(int value) : x(value) {}
// non-const operator=
template<class SrcType>void operator=(SrcType& src)
{
x = src.x;
}
// const operator=
template<class SrcType>void operator=(const SrcType& src)
{
x = src.x;
}
};
int main()
{
CTest nonConstSrc{ 3 };
const CTest constSrc{ 5 };
CTest result;
result = nonConstSrc; // correctly calls non-const operator=
result = constSrc; // ? shallow copy, not calling const operator=
return 0;
}
知道如何让它使用我的重载函数吗?谢谢
您的 const 函数模板未被调用,因为编译器默认生成 默认复制赋值运算符,其签名为 operator=(const CTest&)
。当编译器必须在非模板函数和模板函数之间进行选择时(当两者具有相同的匹配项时),前者是首选。这就是为什么您的模板化方法没有被调用的原因。
为了帮助编译器达到你想要的 select 版本,添加:
CTest& operator=(const volatile CTest&) = delete;
在上面,你禁用了 normal operator=,这里的 volatile
很重要,没有它编译器会抱怨 operator=
被禁用。通过添加 volatile,您只是使模板版本比 volatile 匹配得更好。
其余不变:
template<class SrcType>
void operator=(SrcType& src)
{
x = src.x;
puts("boom1");
}
template<class SrcType>
void operator=(const SrcType& src) {
x = src.x;
puts("boom2");
}
如果 class 没有声明复制赋值,编译器将隐式生成一个。
您 class 没有复制赋值运算符,它只有模板赋值运算符。
当使用 const
对象调用时,隐式声明的赋值运算符在重载解析期间更匹配。
这里的问题是我们不能删除隐式声明的赋值运算符,因为它会产生编译器错误。然而,我们可以编写自己的赋值运算符转发到我们的模板。
#include <iostream>
class CTest
{
public:
int x{};
CTest() = default;
CTest(int value) : x(value) {}
CTest(const CTest& v) = delete;
CTest& operator=(const CTest& v) {
return operator=<CTest>(v);
}
// non-const operator=
template<class SrcType>CTest& operator=(SrcType& src)
{
x = src.x;
std::cout << "non-const\n";
return *this;
}
template<class SrcType>CTest& operator=(const SrcType& src)
{
x = src.x;
std::cout << "const\n";
return *this;
}
};
int main()
{
CTest nonConstSrc{ 3 };
const CTest constSrc{ 5 };
CTest result;
result = nonConstSrc; // correctly calls non-const operator=
result = constSrc; // calls copy-assignment and forwards to template
return 0;
}
这是在 c++ 中,使用 Visual Studio 2019(没有尝试过其他编译器)。 我想添加一个模板化的 operator= 方法。如果参数是非常量,它工作正常。但是如果参数是常量,即使我制作一个带有常量参数的版本,它也不会被调用。相反,它做一个简单的浅拷贝。
如果我使用命名函数而不是运算符,它会按预期工作。同样,如果它不是模板化的,则按预期调用运算符。组合似乎是问题所在。
这是一个展示问题的示例。
class CTest
{
public:
int x{};
CTest() = default;
CTest(int value) : x(value) {}
// non-const operator=
template<class SrcType>void operator=(SrcType& src)
{
x = src.x;
}
// const operator=
template<class SrcType>void operator=(const SrcType& src)
{
x = src.x;
}
};
int main()
{
CTest nonConstSrc{ 3 };
const CTest constSrc{ 5 };
CTest result;
result = nonConstSrc; // correctly calls non-const operator=
result = constSrc; // ? shallow copy, not calling const operator=
return 0;
}
知道如何让它使用我的重载函数吗?谢谢
您的 const 函数模板未被调用,因为编译器默认生成 默认复制赋值运算符,其签名为 operator=(const CTest&)
。当编译器必须在非模板函数和模板函数之间进行选择时(当两者具有相同的匹配项时),前者是首选。这就是为什么您的模板化方法没有被调用的原因。
为了帮助编译器达到你想要的 select 版本,添加:
CTest& operator=(const volatile CTest&) = delete;
在上面,你禁用了 normal operator=,这里的 volatile
很重要,没有它编译器会抱怨 operator=
被禁用。通过添加 volatile,您只是使模板版本比 volatile 匹配得更好。
其余不变:
template<class SrcType>
void operator=(SrcType& src)
{
x = src.x;
puts("boom1");
}
template<class SrcType>
void operator=(const SrcType& src) {
x = src.x;
puts("boom2");
}
如果 class 没有声明复制赋值,编译器将隐式生成一个。
您 class 没有复制赋值运算符,它只有模板赋值运算符。
当使用 const
对象调用时,隐式声明的赋值运算符在重载解析期间更匹配。
这里的问题是我们不能删除隐式声明的赋值运算符,因为它会产生编译器错误。然而,我们可以编写自己的赋值运算符转发到我们的模板。
#include <iostream>
class CTest
{
public:
int x{};
CTest() = default;
CTest(int value) : x(value) {}
CTest(const CTest& v) = delete;
CTest& operator=(const CTest& v) {
return operator=<CTest>(v);
}
// non-const operator=
template<class SrcType>CTest& operator=(SrcType& src)
{
x = src.x;
std::cout << "non-const\n";
return *this;
}
template<class SrcType>CTest& operator=(const SrcType& src)
{
x = src.x;
std::cout << "const\n";
return *this;
}
};
int main()
{
CTest nonConstSrc{ 3 };
const CTest constSrc{ 5 };
CTest result;
result = nonConstSrc; // correctly calls non-const operator=
result = constSrc; // calls copy-assignment and forwards to template
return 0;
}