是否可以在 C++14 中分配和验证 constexpr 结构?



我在C++中实现了getop()。这是一个有各种花里胡哨的课程。

其中一个铃铛是验证用户在编译时是否输入有效选项的一种方法。我已经有一部分用于标志(此处未显示),但我一直在努力让它适用于struct option,目前我还没有取得多大成功。

首先,代码允许我使用define_option()函数定义选项数组。我希望能够以任何顺序指定选项结构的字段,并对它们进行一些健全性检查。所以我对模板函数使用参数列表。每个参数都是一个类,当场实例化,然后define_option()为每个字段选择正确的类,提取值。

到这里为止,一切都像魅力一样。非常基本的C++。

我遇到问题的地方是在define_option()类内部。 例如,假设我要尝试以下方法:

... define_option(...)
{
...
option opt = { ... };
static_assert(opt.f_short_name != '', "this failed");
...
}

在这里我得到一个错误。static_assert()告诉我,opt不是恒定的。

A.cpp:168:1:错误:静态断言的非恒定条件

因此,接下来我的步骤是将opt变量标记为constexpr(错误说要解决static_assert()

... define_option(...)
{
...
constexpr option opt = { ... };
...
}

现在即使没有static_assert(),代码也无法编译。我收到以下错误:

A.cpp:168:5:错误:"args#0"不是常量表达式

我认为从define_option()函数中它并不真正可行,即使可以检查结果的选项列表,如下面的工作示例所示。所以最终opt全局变量是一个在编译时完全定义的静态变量(无需代码在运行时重新处理表),但这对我没有帮助,因为我无法确保每个选项的参数将按预期工作。

我有一个关于大肠杆菌的在线运行示例。

// compiled with: g++ -std=c++14 -o a ~/tmp/a.cpp
//
#include <iostream>
typedef char32_t            short_name_t;
constexpr short_name_t      NO_SHORT_NAME = L'';
struct option
{
short_name_t        f_short_name = NO_SHORT_NAME;
char const *        f_name = nullptr;
};

template<typename T, T default_value>
class OptionValue
{
public:
typedef T   value_t;
constexpr OptionValue<T, default_value>()
: f_value(default_value)
{
}
constexpr OptionValue<T, default_value>(T const v)
: f_value(v)
{
}
constexpr value_t get() const
{
return f_value;
}
private:
value_t     f_value;
};

class ShortName
: public OptionValue<short_name_t, NO_SHORT_NAME>
{
public:
constexpr ShortName()
: OptionValue<short_name_t, NO_SHORT_NAME>()
{
}
constexpr ShortName(short_name_t name)
: OptionValue<short_name_t, NO_SHORT_NAME>(name)
{
}
};

class Name
: public OptionValue<char const *, nullptr>
{
public:
constexpr Name()
: OptionValue<char const *, nullptr>()
{
}
constexpr Name(char const * name)
: OptionValue<char const *, nullptr>(name)
{
}
};

template<typename T, typename F, class ...ARGS>
constexpr typename std::enable_if<std::is_same<T, F>::value, typename T::value_t>::type find_option(F first, ARGS ...args)
{
return first.get();
}

template<typename T, typename F, class ...ARGS>
constexpr typename std::enable_if<!std::is_same<T, F>::value, typename T::value_t>::type find_option(F first, ARGS ...args)
{
return find_option<T>(args...);
}

template<class ...ARGS>
constexpr option define_option(ARGS ...args)
{
option opt =
{
.f_short_name =          find_option<ShortName   >(args..., ShortName()),
.f_name =                find_option<Name        >(args...),
};
return opt;
}

constexpr option const opt[] =
{
define_option(
Name("--help"),
ShortName('h')
)
};

// this test works as expected here
// but it does not work from inside define_option()
//
static_assert(opt[0].f_name[0] != '-', "Wrong start?!");
int main(int argc, char * argv [])
{
std::cerr << "opt[0].f_short_name = " << opt[0].f_short_name << std::endl;
return 0;
}

目前我被限制为C++14。如果有 C++17 解决方案,我仍然会感兴趣。

只需添加一些使调用非常量的内容。比如说,抛出一个例外。

template<class ...ARGS>
constexpr option define_option(ARGS ...args)
{
option opt =
{
.f_short_name =          find_option<ShortName   >(args..., ShortName()),
.f_name =                find_option<Name        >(args...),
};
if(/* check fails */) throw something;
return opt;
}

然后当你尝试做

constexpr option const opt[] =
{
define_option(
Name("--help"),
ShortName('h')
)
};

并且检查失败,编译器将抱怨此对define_option的调用不是常量表达式,不能用于初始化constexpr变量。

如果此函数在运行时调用,您甚至可以something绑定以生成链接器错误。例如,请参阅此问题。

相关内容

  • 没有找到相关文章

最新更新