我有这段代码可以在GCC 9.1中正常工作:
#include <type_traits>
template< typename T >
class A
{
protected:
T value;
public:
template< typename U,
typename...,
typename = std::enable_if_t< std::is_fundamental< U >::value > >
A& operator=(U v)
{
value = v;
return *this;
}
};
template< typename T >
class B : public A<T>
{
public:
using A<T>::operator=;
template< typename U,
typename...,
typename = std::enable_if_t< ! std::is_fundamental< U >::value > >
B& operator=(U v)
{
this->value = v;
return *this;
}
};
int main()
{
B<int> obj;
obj = 2;
}
(实际上,我们会在
B::operator=
中做一些花哨的事情,甚至为enable_if
使用不同的类型特征,但这是最简单的可重现示例。)问题是thlang Clang 8.0.1给出了一个错误,尽管子级有
operator=
,但不考虑父类的using A<T>::operator=;
:test.cpp:39:9: error: no viable overloaded '='
obj = 2;
~~~ ^ ~
test.cpp:4:7: note: candidate function (the implicit copy assignment operator) not viable:
no known conversion from 'int' to 'const A<int>' for 1st argument
class A
^
test.cpp:4:7: note: candidate function (the implicit move assignment operator) not viable:
no known conversion from 'int' to 'A<int>' for 1st argument
class A
^
test.cpp:20:7: note: candidate function (the implicit copy assignment operator) not
viable: no known conversion from 'int' to 'const B<int>' for 1st argument
class B : public A<T>
^
test.cpp:20:7: note: candidate function (the implicit move assignment operator) not
viable: no known conversion from 'int' to 'B<int>' for 1st argument
class B : public A<T>
^
test.cpp:28:8: note: candidate template ignored: requirement
'!std::is_fundamental<int>::value' was not satisfied [with U = int, $1 = <>]
B& operator=(U v)
^
1 error generated.
哪个编译器符合标准? (我正在使用
-std=c++14
进行编译。)如何更改代码以使其正确? 最佳答案
考虑以下简化代码:
#include <iostream>
struct A
{
template <int n = 1> void foo() { std::cout << n; }
};
struct B : public A
{
using A::foo;
template <int n = 2> void foo() { std::cout << n; }
};
int main()
{
B obj;
obj.foo();
}
这将在两个编译器上均显示2。
如果派生类已经具有一个具有相同签名的类,则它将隐藏或覆盖
using
声明引入的类。表面上,赋值运算符的签名是相同的。考虑以下片段:template <typename U,
typename = std::enable_if_t<std::is_fundamental<U>::value>>
void bar(U) {}
template <typename U,
typename = std::enable_if_t<!std::is_fundamental<U>::value>>
void bar(U) {}
这将导致两个编译器的
bar
重定义错误。但是,如果更改其中一个模板的返回类型,该错误就会消失!
现在是时候仔细研究一下标准了。
现在,就模板而言,这听起来令人怀疑。如果不比较模板参数列表,又怎能比较两个参数类型列表呢?前者取决于后者。确实,以上一段说:
这更有意义。如果两个模板的模板参数列表相同,则其他两个模板也是相同的……但是等等,这包括返回类型!如果两个模板的名称和签名中的所有内容(包括返回类型(但不包括默认参数值))相同,则它们是相同的。然后,一个可以与另一个冲突或隐藏。
那么,如果我们在B中更改赋值运算符的返回类型并使它与A中的相同,会发生什么? GCC停止接受该代码。
所以我的结论是这样的:
using
和using
之间的标准语言都存在无法解释的不一致,这会将基类名称引入派生类。 using
的规则,并将其应用于基类/派生类的上下文中。 关于c++ - 继承C++ 14 : different behaviour with g++ and clang++中的模板化operator =,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57322624/