尝试使用继承和模板实现CRTP.Visual Studio正在生成编译器错误

Trying to implement CRTP using inheritance and templates. Visual Studio is Generating Compiler Errors

本文关键字:Studio Visual 错误 编译器 CRTP 实现 继承      更新时间:2023-10-16

这是上一个问题的延续,删除了代码重复,用户1201programalarm建议使用CRTP来解决我的问题。我正在尝试接受他们的建议,当我尝试将其应用于我的Bus类时,Visual Studio正在生成编译器错误。。。

当我尝试这样做时:

template<size_t BusSize>
class Bus : public Component, ComponentID<Bus> {

我得到这个错误:

>------ Build started: Project: Simulator, Configuration: Debug x64 ------
1>main.cpp
1>c:***bus.h(6): error C3203: 'Bus': unspecialized class template can't be used as a template argument for template parameter 'T', expected a real type
1>c:***bus.h(49): note: see reference to class template instantiation 'Bus<BusSize>' being compiled
1>Done building project "Simulator.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

当我尝试将其更改为:

template<size_t BusSize>
class Bus : public Component, ComponentID<Bus<BusSize>> {

然后它给了我这些错误:

>------ Build started: Project: Simulator, Configuration: Debug x64 ------
1>main.cpp
1>c:***bus.h(11): error C2512: 'ComponentID<Bus<4>>': no appropriate default constructor available
1>c:***bus.h(6): note: see declaration of 'ComponentID<Bus<4>>'
1>c:***bus.h(11): note: while compiling class template member function 'Bus<4>::Bus(const std::string &)'
1>c:***main.cpp(10): note: see reference to function template instantiation 'Bus<4>::Bus(const std::string &)' being compiled
1>c:***main.cpp(10): note: see reference to class template instantiation 'Bus<4>' being compiled
1>c:***bus.h(11): error C2614: 'Bus<4>': illegal member initialization: 'ComponentID' is not a base or member
1>Done building project "Simulator.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

我不确定还有什么可以解决这些编译器错误。。。

这是我的源代码。。。

main.cpp

#include <exception>
#include "Bus.h"
int main() {
try {
Wire w1, w2, w3, w4;
std::cout << w1.id() << " " << w2.id() << " " << w3.id() << " "<< w4.id() << "nn";
Bus<4> b1;
b1.connect(&w1, 0);
b1.connect(&w2, 1);
b1.connect(&w3, 2);
b1.connect(&w4, 3);
b1.connect(&w1, 1);  // Valid Connection: same wire to multiple bus wires
b1.connect(&w1, 0);  // Invalid Connection: trying to connect a same wire to the same bus wire multiple times...
std::cout << "n";
Bus<4> b2;
w1.connect(&w2);
w2.connect(&w3);
w3.connect(&w4);
w4.connect(&w1);
std::cout << "n";
b2.connect(&b1.wire(0), 0);
b2.connect(&b1.wire(1), 1);
b2.connect(&b1.wire(2), 2);
b2.connect(&b1.wire(3), 3);
std::cout << "nThese are my connected components:n";
for( size_t i = 0; i < b2.size(); i++ ) {
for (auto& component : b2.myConnections(i)) {
std::cout << "t" << component->id() << " has ";
auto connections = component->myConnections();
for (auto& c : connections) {
std::cout << c->id() << " ";
}
std::cout << 'n';
}
std::cout << 'n';
}

} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

组件.h

#pragma once
#include <assert.h>
#include <memory>  
#include <array>
#include <list>       
#include <iomanip>
#include <iostream>    
#include <string>
#include <sstream>    
// I added this class template to handle the `ID's` as suggested to remove the code duplication...
template<class T>
class ComponentID {
public:
explicit ComponentID(std::string& id) {
static int i = 0;
std::stringstream strValue;
strValue << std::setw(3) << std::setfill('0') << std::to_string(i++);
id.append("_" + strValue.str());
}
};
class Component {
protected:
std::string id_ = "";
std::list<std::shared_ptr<Component>> components_;
explicit Component(const std::string& id) : id_{ id } {}
public:
virtual ~Component() {}
std::string& id() { return id_; }
void connect(Component* other) {
for (auto& l : components_) {
if (other->id_ == l->id()) {
std::cout << "Component " << other->id_ << " already exists in " << id_ << "!n";
return;
}
}
components_.push_back( std::make_shared<Component>( *other ) );
std::cout << "Successfully connected " << other->id() << " to " << id_ << "n";
}
virtual std::list<std::shared_ptr<Component>> myConnections() { return components_; }
virtual void propagate() {};
};

Wire.h

#pragma once
#include "Component.h"
// I added ComponentID as an inherited base,
// I removed the `updateID()` function from this class,
// and I removed the call to it from within the constructor
class Wire : public Component, ComponentID<Wire> {
private:
public:
explicit Wire(const std::string& name = "Wire") : Component(name), ComponentID(id_) {
//updateId();
};

virtual ~Wire() {}
virtual void propagate() override {
return;
}        
};

现在,这是我开始有一些问题的地方。。。

总线h

#pragma once
#include "Wire.h"
// I'm trying to do the same as I did in my Wire class through inheritance...
// I also removed the updateID() function and the call to it with the constructor
// However, this is generating a compiler error.
template<size_t BusSize>
class Bus : public Component, ComponentID<Bus> {
private:
std::array<std::shared_ptr<Wire>, BusSize> bus_interface_;
public:
explicit Bus(const std::string& name = "Bus") : Component(name), ComponentID(id_) {        
createBus();
}
virtual ~Bus() {}
virtual void propagate() override {
return;
}
void connect(Component* component, size_t index) {
assert(index < BusSize);
assert(component != nullptr);
bus_interface_[index]->connect(component);
std::cout << "t from " << component->id() << " to " << id_ << "n";
}
Wire wire(size_t index) {
assert(index < BusSize);
return *bus_interface_[index].get();
}
std::array<std::shared_ptr<Wire>, BusSize> bus() { return bus_interface_; }
std::list<std::shared_ptr<Component>> myConnections(size_t index) { 
assert(index < BusSize);
return bus_interface_[index]->myConnections();
}
size_t size() const { return BusSize; }
private:
void createBus() {
size_t i = 0;
for (auto& w : bus_interface_) {
w = std::shared_ptr<Wire>(new Wire);
}
}
};

您第二次尝试设置基类是正确的,您只需要添加对基类构造函数的调用,因为它没有默认构造函数:

template<size_t BusSize>
class Bus : public Component , ComponentID<Bus<BusSize>> {
private:
std::array<std::shared_ptr<Wire>, BusSize> bus_interface_;
public:
explicit Bus(const std::string& name = "Bus") : Component(name), ComponentID<Bus<BusSize>>(id_) {        
createBus();
}

通常使用类型别名来简化这一点:

template<size_t BusSize>
class Bus : public Component , ComponentID<Bus<BusSize>> {
private:
using BusComponentID = ComponentID<Bus<BusSize>>;
std::array<std::shared_ptr<Wire>, BusSize> bus_interface_;
public:
explicit Bus(const std::string& name = "Bus") : Component(name), BusComponentID(id_) {        
createBus();
}

我找到了解决问题的方法。。。我仍然困惑于为什么Visual Studio会给我编译器错误。。。

在查看了代码并看到了正在使用的CRTP的模式后,在没有修改它的情况下…我找到了一种有效的方法,它对我有效…

在我的Bus类中,它本身是模板化的,但没有typenametype。。。这让我思考并知道,当前的CRTP对于非模板化的Wire类工作得很好。。。我选择做以下事情。。。

我创建了另一个抽象基类。。。即介于CCD_ 8和CCD_ 9之间的中间层。。。正是这个非模板化的抽象类继承了ComponentComponentID。那么我只需要从BusAbstract继承Bus<size_t>

我的其余代码仍然是一样的,除了在它们的构造函数中对自动命名-id生成做了一些小的更改。。。这是我的Bus课程的结果。

总线h

#pragma once
#include "Wire.h"
class BusAbstract : public Component, ComponentID<BusAbstract> {
protected:
explicit BusAbstract(const std::string& name = "Bus") : Component(name), ComponentID(id_) {}
};
template<size_t BusSize>
class Bus : public BusAbstract {
private:
std::array<std::shared_ptr<Wire>, BusSize> bus_interface_;
public:
explicit Bus(const std::string& name = ("Bus<" + std::to_string(BusSize) + ">")  ) : BusAbstract(name) {        
createBus();
}
virtual ~Bus() {}
virtual void propagate() override {
return;
}
void connect(Component* component, size_t index) {
assert(index < BusSize);
assert(component != nullptr);
bus_interface_[index]->connect(component);
std::cout << "t from " << component->id() << " to " << id_ << "n";
}
Wire wire(size_t index) {
assert(index < BusSize);
return *bus_interface_[index].get();
}
std::array<std::shared_ptr<Wire>, BusSize> bus() { return bus_interface_; }
virtual std::list<std::shared_ptr<Component>> myConnections(size_t index) { 
assert(index < BusSize);
return bus_interface_[index]->myConnections();
}
size_t size() const { return BusSize; }
private:
void createBus() {
size_t i = 0;
for (auto& w : bus_interface_) {
w = std::shared_ptr<Wire>(new Wire(this->id() + "_Wire"));
}
}
};

现在我得到了我想要的行为,而没有了";"代码复制";根据最初的问题,我可以使用这个CRTP设计来做到这一点。