我正在尝试编写一个处理数据的通用组件,该组件当前由枚举标识。数据可以是不同的类型,但是一个id只能是一种类型。
我想要实现的是在代码库中的某个地方对模板进行专用化,并仅通过其标识符来调用函数,例如
Test::get<ID2>()
在下面的代码(包括类型)中有效的内容。是否有一种简单/智能的方法来避免在get行中编写类型?
我尝试使用typedef进行标识,当然可以直接将其解析为原始类型,这两个模板的专业性是模棱两可的。如果没有好的C ++方法,我仍然可以使用代码生成来解决该问题,但我希望有一个更好的解决方案。
#include <iostream>
typedef int ID1_t;
typedef double ID2_t;
enum {
ID1,
ID2,
};
class Test
{
public:
template<int I, typename T> static T get();
};
template<> int Test::get<ID1>()
{
return 43;
}
template<> double Test::get<ID2>()
{
return 0.12;
}
int main()
{
std::cout << Test::get<ID2, ID2_t>() << std::endl;
std::cout << Test::get<ID1, ID1_t>() << std::endl;
}
编辑(更多背景信息): 我正在编写一个使用组件获取数据(如int)并使用通知机制将数据设置为in的中央控件,以告知所有需要的位置。 它的中心定义但分散实现,该接口对于所有用途都是相同的,但是实现可以在自己的源文件中。 这样可以大大减少组件之间的依赖关系,而且还可以进行强大的类型化数据交换。
例如:
可以说,我们在生产时就将数据写入闪存中,将配置数据存储在EEPROM或FRAM中,并且将RAM变量中的一些易失性数据在电源重启时会忘记。
让扬声器获取可以以某种方式更改的音量值。我只想将值放入系统中:
auto volume = 55; //(constant here but is in real decoded from SPI)
CentralDataStorage::set<Volume>(volume);
我不想在这里依赖于物理存储。 比我想要一个负责存储值并根据要求返回值的组件。因此,它仅实现id Volume 的set和get方法。
另一个组件,它实际上是将音量设置到放大器中,它会从中央接口获得通知并使用get方法,该接口在该组件不知道的地方实现。
auto volume = CentralDataStorage::get<Volume>();
所有内容在编译时都是已知的,需要类型安全。
答案 0 :(得分:2)
我将首先解释我做这种事情的首选方式。我通常会尽量避免使用枚举,因为几乎每次您都需要其他数据时,由于使用了枚举,您不得不将数据写入其他位置。例如,其他地方可能是模板专业化。
我宁愿使用类型:
struct ID1 {
static constexpr auto default_value = 42;
};
struct ID2 {
static constexpr auto default_value = 0.12;
};
然后删除专业化并使用以下类型中提供的数据:
struct Test {
template<typename T>
static auto get() {
// or maybe call a static member function
return T::default_value;
}
};
int main()
{
std::cout << Test::get<ID2>() << std::endl;
std::cout << Test::get<ID1>() << std::endl;
}
但是有一种方法可以使您的设计工作并避免使用返回类型推导来编写该类型:
#include <iostream>
enum {
ID1,
ID2,
};
struct Test {
// to be deduced -------v
template<auto I> static auto get();
// ^--- (optional change)
// Have the enum type deduce as well
};
template<> auto Test::get<ID1>()
{
return 43;
}
template<> auto Test::get<ID2>()
{
return 0.12;
}
int main()
{
std::cout << Test::get<ID2>() << std::endl;
std::cout << Test::get<ID1>() << std::endl;
}
See it compiling on compiler explorer。
您还可以返回到int
,因为枚举模板参数与C ++ 14保持兼容:
template<int I> static auto get();
// ^------ reverted it back to int
但是使用C ++ 17和自动模板参数,您可以拥有多个枚举而不会发生值冲突:
enum struct A {
Value1, Value2
};
enum struct B {
Value1
};
// Different with C++17 auto template parameter
// not possible with C++14
template<> auto Test::get<A::Value1>()
{
return 43;
}
template<> auto Test::get<B::Value1>()
{
return 0.12;
}
答案 1 :(得分:2)
我找到了使用其他答案的一部分的方法,并在此处显示了实现:
用于定义所有ID和类型的中央头文件。 decltype 有助于查找返回类型。在这里使用 auto 会导致我遇到以下问题:当我在此处为特定类型声明方法时,将与模板声明不匹配
//Test.hpp
struct ID1 {
int value;
};
struct ID2 {
double value;
};
struct Test {
template<typename T> static decltype(T::value) get();
};
然后我定义了get函数(也可以在单独的编译单元中)
#include "Test.hpp"
template<> int Test::get<ID1>()
{
return 43;
}
template<> double Test::get<ID2>()
{
return 0.12;
}
和使用它们的主体
#include "Test.hpp"
int main()
{
std::cout << Test::get<ID2>() << std::endl;
std::cout << Test::get<ID1>() << std::endl;
}
这几乎是乔治·库尔蒂斯(George Kourtis)和纪尧姆·拉西科特(Guillaume Racicot)的答案,并给我自己加上了一点。
答案 2 :(得分:1)
我想你想要这个吗?
#include <iostream>
typedef int ID1;
typedef double ID2;
class Test
{
public:
template<typename T> static T get();
};
template<> int Test::get<ID1>()
{
return 43;
}
template<> double Test::get<ID2>()
{
return 0.12;
}
int main()
{
std::cout << Test::get<ID2>() << std::endl;
std::cout << Test::get<ID1>() << std::endl;
}
这个想法是,如果数据类型在编译时是已知的,那么就不需要写数字来选择数据的类型。 数据类型本身足以完成任务。
答案 3 :(得分:0)
您的意思是将声明一分为二吗?
template<int i> struct id_type;
template<int i> using id_type_t = typename id_type<i>::type;
template<> struct id_type<ID1> { using type = int; }
template<int i> id_type_t<i> get();