混合模板、多重继承和非默认构造函数

Mixing Templates, Multiple Inheritance, and non-Default Constructor

提问人:Mad Physicist 提问时间:11/3/2023 最后编辑:Mad Physicist 更新时间:11/3/2023 访问量:60

问:

我有一个实现某些功能的基类,以及一个虚拟扩展它的模板类。基类具有非默认构造函数,因为它需要一些配置数据。该模板具有默认构造函数。

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

如果我尝试添加对构造函数的调用,例如发生类似的事情:BaseMultiTestexplicit 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>&&’

我需要做些什么才能完成这项工作?另外,为什么?MultiTestmain

C++ 模板 虚拟继承菱 形问题

评论


答:

1赞 Paul Sanders 11/3/2023 #1

你需要提供一些额外的构造函数(如果你使用 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)); }
};

可能有更短的解决方案。顺便说一句,不能说我非常喜欢整个想法。

评论

1赞 Mad Physicist 11/3/2023
我发现这需要一个默认构造函数,这样我们就可以避免通过其他类弄乱它。Base
1赞 Mad Physicist 11/3/2023 #2

由于没有默认构造函数,因此也不能。解决此问题的一种方法是给出一个空构造函数:BaseTestBaseprotected

protected:
    Base(int data) m_data{} {}

现在,空的构造函数不会被删除,我们可以创建一个只需要初始化的构造函数: 。TestBaseMultiTest(int data): Base{data} {}

评论

0赞 Paul Sanders 11/3/2023
吹毛求疵,但这是默认构造函数吗?
1赞 Mad Physicist 11/3/2023
@PaulSanders。好电话。更新为空
0赞 Paul Sanders 11/3/2023
我假设 Base 构造函数应该初始化......(对不起,应该之前发现这一点)。m_data
1赞 Mad Physicist 11/3/2023
@PaulSanders。完全
1赞 Rerito 11/3/2023 #3

您的模板旨在根据您的暗示与多个专业相结合。但是,在这种组合中,所有专业化必须共享相同(因此是虚拟继承)。另一种看待这个问题的方法是避免虚拟继承,并执行如下操作:TestMultiTestTestBase

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 >

您可以使用更高级的元编程位来防止这种行为。

完整示例:https://coliru.stacked-crooked.com/a/4092d65e35571b41