提问人:Mad Physicist 提问时间:11/3/2023 最后编辑:Mad Physicist 更新时间:11/3/2023 访问量:60
混合模板、多重继承和非默认构造函数
Mixing Templates, Multiple Inheritance, and non-Default Constructor
问:
我有一个实现某些功能的基类,以及一个虚拟扩展它的模板类。基类具有非默认构造函数,因为它需要一些配置数据。该模板具有默认构造函数。
class Base
{
public:
explicit Base(int data): m_data{data} {}
int boilerplate(int x) const { return x + m_data; }
protected:
int m_data;
};
template<typename T>
class Test: public virtual Base
{
public:
virtual int foo(T x) const { return x + m_data; } // works by default for integers for convenience
};
我正在尝试创建一个继承自模板的多个专用化的类,该类可以将相同的数据传递给单个虚拟基类。
class MultiTest : public Test<float>, public Test<double>
{
public:
int foo(float x) const override { return x * static_cast<float>(boilerplate(3)); }
int foo(double x) const override { return x / static_cast<double>(boilerplate(10)); }
};
如果我使用整数参数实例化类,则会出现错误:
int main() { MultiTest x{32}; }
$ g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:24:26: error: no matching function for call to ‘MultiTest::MultiTest(<brace-enclosed initializer list>)’
24 | int main() { MultiTest x{32}; }
| ^
test.cpp:17:7: note: candidate: ‘MultiTest::MultiTest(const MultiTest&)’
17 | class MultiTest : public Test<float>, public Test<double>
| ^~~~~~~~~
test.cpp:17:7: note: no known conversion for argument 1 from ‘int’ to ‘const MultiTest&’
test.cpp:17:7: note: candidate: ‘MultiTest::MultiTest(MultiTest&&)’
test.cpp:17:7: note: no known conversion for argument 1 from ‘int’ to ‘MultiTest&&’
我不能做 Base' 类没有默认构造函数:int main() { MultiTest x; } with no arguments either of course, since the
$ g++ test.cpp
test.cpp: In function ‘int main()’:
test.cpp:24:24: error: use of deleted function ‘MultiTest::MultiTest()’
24 | int main() { MultiTest x; }
| ^
test.cpp:17:7: note: ‘MultiTest::MultiTest()’ is implicitly deleted because the default definition would be ill-formed:
17 | class MultiTest : public Test<float>, public Test<double>
| ^~~~~~~~~
test.cpp:17:7: error: use of deleted function ‘Test<float>::Test()’
test.cpp:11:7: note: ‘Test<float>::Test()’ is implicitly deleted because the default definition would be ill-formed:
11 | class Test: public virtual Base
| ^~~~
test.cpp:11:7: error: no matching function for call to ‘Base::Base()’
test.cpp:4:14: note: candidate: ‘Base::Base(int)’
4 | explicit Base(int data): m_data{data} {}
| ^~~~
test.cpp:4:14: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr Base::Base(const Base&)’
1 | class Base
| ^~~~
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr Base::Base(Base&&)’
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
test.cpp:17:7: error: use of deleted function ‘Test<double>::Test()’
17 | class MultiTest : public Test<float>, public Test<double>
| ^~~~~~~~~
test.cpp:11:7: note: ‘Test<double>::Test()’ is implicitly deleted because the default definition would be ill-formed:
11 | class Test: public virtual Base
| ^~~~
test.cpp:11:7: error: no matching function for call to ‘Base::Base()’
test.cpp:4:14: note: candidate: ‘Base::Base(int)’
4 | explicit Base(int data): m_data{data} {}
| ^~~~
test.cpp:4:14: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr Base::Base(const Base&)’
1 | class Base
| ^~~~
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr Base::Base(Base&&)’
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
test.cpp:17:7: error: no matching function for call to ‘Base::Base()’
17 | class MultiTest : public Test<float>, public Test<double>
| ^~~~~~~~~
test.cpp:4:14: note: candidate: ‘Base::Base(int)’
4 | explicit Base(int data): m_data{data} {}
| ^~~~
test.cpp:4:14: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr Base::Base(const Base&)’
1 | class Base
| ^~~~
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr Base::Base(Base&&)’
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
如果我尝试添加对构造函数的调用,例如发生类似的事情:Base
MultiTest
explicit MultiTest(int data): Base{data} {}
$ g++ test.cpp
test.cpp: In constructor ‘MultiTest::MultiTest(int)’:
test.cpp:20:44: error: use of deleted function ‘Test<float>::Test()’
20 | explicit MultiTest(int data): Base{data} {}
| ^
test.cpp:11:7: note: ‘Test<float>::Test()’ is implicitly deleted because the default definition would be ill-formed:
11 | class Test: public virtual Base
| ^~~~
test.cpp:11:7: error: no matching function for call to ‘Base::Base()’
test.cpp:4:14: note: candidate: ‘Base::Base(int)’
4 | explicit Base(int data): m_data{data} {}
| ^~~~
test.cpp:4:14: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr Base::Base(const Base&)’
1 | class Base
| ^~~~
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr Base::Base(Base&&)’
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
test.cpp:20:44: error: use of deleted function ‘Test<double>::Test()’
20 | explicit MultiTest(int data): Base{data} {}
| ^
test.cpp:11:7: note: ‘Test<double>::Test()’ is implicitly deleted because the default definition would be ill-formed:
11 | class Test: public virtual Base
| ^~~~
test.cpp:11:7: error: no matching function for call to ‘Base::Base()’
test.cpp:4:14: note: candidate: ‘Base::Base(int)’
4 | explicit Base(int data): m_data{data} {}
| ^~~~
test.cpp:4:14: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr Base::Base(const Base&)’
1 | class Base
| ^~~~
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr Base::Base(Base&&)’
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
我也试过:explicit MultiTest(int data): Test<float>{data}, Test<double>{data} {}
$ g++ test.cpp
test.cpp: In constructor ‘MultiTest::MultiTest(int)’:
test.cpp:20:71: error: no matching function for call to ‘Base::Base()’
20 | explicit MultiTest(int data): Test<float>{data}, Test<double>{data} {}
| ^
test.cpp:4:14: note: candidate: ‘Base::Base(int)’
4 | explicit Base(int data): m_data{data} {}
| ^~~~
test.cpp:4:14: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr Base::Base(const Base&)’
1 | class Base
| ^~~~
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr Base::Base(Base&&)’
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
test.cpp:20:71: error: no matching function for call to ‘Test<float>::Test(<brace-enclosed initializer list>)’
20 | explicit MultiTest(int data): Test<float>{data}, Test<double>{data} {}
| ^
test.cpp:11:7: note: candidate: ‘Test<float>::Test(const Test<float>&)’
11 | class Test: public virtual Base
| ^~~~
test.cpp:11:7: note: no known conversion for argument 1 from ‘int’ to ‘const Test<float>&’
test.cpp:11:7: note: candidate: ‘Test<float>::Test(Test<float>&&)’
test.cpp:11:7: note: no known conversion for argument 1 from ‘int’ to ‘Test<float>&&’
test.cpp:20:71: error: no matching function for call to ‘Test<double>::Test(<brace-enclosed initializer list>)’
20 | explicit MultiTest(int data): Test<float>{data}, Test<double>{data} {}
| ^
test.cpp:11:7: note: candidate: ‘Test<double>::Test(const Test<double>&)’
11 | class Test: public virtual Base
| ^~~~
test.cpp:11:7: note: no known conversion for argument 1 from ‘int’ to ‘const Test<double>&’
test.cpp:11:7: note: candidate: ‘Test<double>::Test(Test<double>&&)’
test.cpp:11:7: note: no known conversion for argument 1 from ‘int’ to ‘Test<double>&&’
并得到了类似的结果:explicit MultiTest(int data): Test<float>{data}, Test<double>{data}, Base{data} {}
$ g++ test.cpp
test.cpp: In constructor ‘MultiTest::MultiTest(int)’:
test.cpp:20:83: error: no matching function for call to ‘Test<float>::Test(<brace-enclosed initializer list>)’
20 | explicit MultiTest(int data): Test<float>{data}, Test<double>{data}, Base{data} {}
| ^
test.cpp:11:7: note: candidate: ‘Test<float>::Test(const Test<float>&)’
11 | class Test: public virtual Base
| ^~~~
test.cpp:11:7: note: no known conversion for argument 1 from ‘int’ to ‘const Test<float>&’
test.cpp:11:7: note: candidate: ‘Test<float>::Test(Test<float>&&)’
test.cpp:11:7: note: no known conversion for argument 1 from ‘int’ to ‘Test<float>&&’
test.cpp:20:83: error: no matching function for call to ‘Test<double>::Test(<brace-enclosed initializer list>)’
20 | explicit MultiTest(int data): Test<float>{data}, Test<double>{data}, Base{data} {}
| ^
test.cpp:11:7: note: candidate: ‘Test<double>::Test(const Test<double>&)’
11 | class Test: public virtual Base
| ^~~~
test.cpp:11:7: note: no known conversion for argument 1 from ‘int’ to ‘const Test<double>&’
test.cpp:11:7: note: candidate: ‘Test<double>::Test(Test<double>&&)’
test.cpp:11:7: note: no known conversion for argument 1 from ‘int’ to ‘Test<double>&&’
我需要做些什么才能完成这项工作?另外,为什么?MultiTest
main
答:
你需要提供一些额外的构造函数(如果你使用 clang 进行编译,编译器错误会说明原因):
template<typename T>
class Test: public virtual Base
{
public:
explicit Test (int data) : Base (data) { } // <==
virtual int foo(T x) const { return x + m_data; } // works by default for integers for convenience
};
和:
class MultiTest : public Test<float>, public Test<double>
{
public:
explicit MultiTest (int data) : Base (data), Test <float> (data), Test <double> (data) { } // <==
int foo(float x) const override { return x * static_cast<float>(boilerplate(3)); }
int foo(double x) const override { return x / static_cast<double>(boilerplate(10)); }
};
可能有更短的解决方案。顺便说一句,不能说我非常喜欢整个想法。
评论
Base
由于没有默认构造函数,因此也不能。解决此问题的一种方法是给出一个空构造函数:Base
Test
Base
protected
protected:
Base(int data) m_data{} {}
现在,空的构造函数不会被删除,我们可以创建一个只需要初始化的构造函数: 。Test
Base
MultiTest(int data): Base{data} {}
评论
m_data
您的模板旨在根据您的暗示与多个专业相结合。但是,在这种组合中,所有专业化必须共享相同(因此是虚拟继承)。另一种看待这个问题的方法是避免虚拟继承,并执行如下操作:Test
MultiTest
Test
Base
class Base
{
public:
explicit Base(int data): m_data{data} {}
int boilerplate(int x) const { return x + m_data; }
protected:
int m_data;
};
template< typename T >
class Test // <- no inheritance
{
public:
int fooImpl(T x) { return x; }
};
template< typename... Ts >
class MultiTest : public Base, Test< Ts >...
{
public:
MultiTest(int data) : Base(data), Test< Ts >()... {}
// You can prettify this for error handling using concepts/static_asserts...
template< typename T >
int foo(T x) { return Test< T >::fooImpl(x) + m_data; }
};
用法与示例中的用法相同:
MultiTest< int, double > m{ 1 };
m.foo(12); // Will call Test< int >::fooImpl
需要注意的是,由于我们有一个模板,因此以下调用将产生编译器错误:
m.foo(12u); // Will call Test< unsigned>::fooImpl but MultiTest< int, double > does not inherit from Test< unsigned >
您可以使用更高级的元编程位来防止这种行为。
评论