在不使用宏的情况下减少语法"噪音"

Ðаn*_*Ðаn 6 c++ macros templates

我试图找到一种方法来减少一些语法"噪音"而不诉诸宏.对于以下代码:

struct base { base() = delete; };    
struct tag1 final : private base
{
    static constexpr const char* name = "tag1";
};
template <typename T> std::string name() { return T::name; }
// ...
int main()
{
   const std::string name1(name<tag1>());
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

这将是很好摆脱的一些static constexpr const char*(更不用提其他)语法,因为它很烦人的重复,对tag2,tag3等等.另外,所有的这是非常有趣的是唯一的一部分tag1,其余的是"噪声".直接的解决方案使用宏:

#define MAKE_TAG(tag_name) struct tag_name final : private base { \
    static constexpr const char* name = #tag_name; }    
MAKE_TAG(tag2);
// ...
const std::string name2(name<tag2>());
Run Code Online (Sandbox Code Playgroud)

基于宏的MAKE_TAG(tag2);语法已经消除了所有"噪音",tag2非常突出.宏的一个额外好处是tag_name可以很容易地将其转换为字符串文字,以防止复制粘贴错误.

一个"明显的"可能的解决方案可能是作为模板参数传递name

template<const char* name> base { ... };
struct tag3 final : private base<"tag3"> {};
Run Code Online (Sandbox Code Playgroud)

但是C++不支持.从下面的答案中,巧妙的解决方法是使用可变参数模板:

template<char... S> struct base { base() = delete;
    static std::string name() { return{ S... }; } };    
struct tag4 final : base<'t', 'a', 'g', '4'> { };
template <typename T> std::string name() { return T::name(); }
Run Code Online (Sandbox Code Playgroud)

这确实减少了很多噪音,但需要写't', 'a', 'g', '4'而不是"tag4".运行时解决方案相当简洁

struct base {
    const std::string name;
    base(const std::string& name) : name(name) {} };    
struct tag5 final : base { tag5() : base("tag5") {} };
template <typename T> std::string name() { return T().name; }
Run Code Online (Sandbox Code Playgroud)

但这并不完全令人满意,因为tag5现在可以实例化,理想情况下没有意义.此外,现在需要写tag5三次,这不是很.

有没有办法进一步简化(即减少打字)上面的代码?...没有使用宏?

And*_*dyG 4

如果您愿意单独输入字符,我们可以执行以下操作:

template<char... S>
struct base { 
    base() = delete;
    static std::string name(){
        return {S...};
    }
};

struct tag1 final : private base<'t','a','g','1'>
{using base::name;};
struct tag2 final : private base<'t','a','g','2'>
{using base::name;};
Run Code Online (Sandbox Code Playgroud)

演示

像这样称呼它:

std::cout << tag1::name() << std::endl;
std::cout << tag2::name() << std::endl;
Run Code Online (Sandbox Code Playgroud)

我必须添加using base::name派生类,因为您使用的是私有继承。如果继承变成protectedor public,那么你就不需要它。

name()基础函数的要点是创建一个字符数组,我们可以从中构造一个字符串。我们使用可变参数包扩展来创建数组。