if constexpr
是摆脱C ++程序中预处理器的重要一步。但是它只适用于函数 - 比如在这个例子中:
enum class OS
{
Linux,
MacOs,
MsWindows,
Unknown
};
#if defined(__APPLE__)
constexpr OS os = OS::MacOs;
#elif defined(__MINGW32__)
constexpr OS os = OS::MsWindows;
#elif defined(__linux__)
constexpr OS os = OS::Linux;
#else
constexpr OS os = OS::Unknown;
#endif
void printSystem()
{
if constexpr (os == OS::Linux)
{
std::cout << "Linux";
}
else if constexpr (os == OS::MacOs)
{
std::cout << "MacOS";
}
else if constexpr (os == OS::MsWindows)
{
std::cout << "MS Windows";
}
else
{
std::cout << "Unknown-OS";
}
}
但是关于摆脱预处理器的梦想并不十分满意 - 因为以下示例不能编译:
1 不能在类定义中使用它来以不同方式定义某些类的成员:
class OsProperties
{
public:
static void printName()
{
std::cout << osName;
}
private:
if constexpr (os == OS::Linux)
{
const char* const osName = "Linux";
}
else if constexpr (os == OS::MacOs)
{
const char* const osName = "MacOS";
}
else if constexpr (os == OS::MsWindows)
{
const char* const osName = "MS Windows";
}
else
{
const char* const osName = "Unknown";
}
};
2 也不适用于非类范围:
if constexpr (os == OS::Linux)
{
const char* const osName = "Linux";
}
else if constexpr (os == OS::MacOs)
{
const char* const osName = "MacOS";
}
else if constexpr (os == OS::MsWindows)
{
const char* const osName = "MS Windows";
}
else
{
const char* const osName = "Unknown";
}
我(几乎)确定这是按照C ++ 17规范if constexpr
仅在函数体内工作 - 但我的问题是:
Q1 如何在函数中实现类似if-constexpr
的效果 - 对于C ++ 1z / C ++ 14中的类和全局范围?我不是在这里要求对模板专业化的另一种解释......但是与if constexpr
具有相似简单性的东西......
Q2 有没有计划为上述范围扩展C ++?
答案 0 :(得分:12)
如何在函数中实现类似if-constexpr的效果 - 对于C ++ 1z / C ++ 14中的类和全局范围?我不是在这里要求对模板专业化的另一种解释......
你基本上只是说,“我想要模板专业化,但没有那些讨厌的模板专业化。”
if constexpr
是使函数行为基于编译时结构更改的工具。模板专门化是C ++提供的工具,用于使定义基于编译时构造进行更改。它是C ++为此功能提供的唯一工具。
现在,对于初始化变量的简单情况,您始终可以创建并调用lambda。 C ++ 17为lambda提供constexpr
支持,lambda可以使用if constexpr
来决定返回什么值。
是否有计划为上述范围扩展C ++?
没有。 Here are all of the proposals,过去几年没有一个人深入研究这个领域。
这种情况极不可能。
答案 1 :(得分:3)
索引类型:
template<std::size_t I>
using index = std::integral_constant<std::size_t, I>;
first_truth
接受一组编译时bool并说明第一个索引在编译时是什么。如果你传递N编译时bools,如果all都是false,则返回N:
constexpr index<0> first_truth() { return {}; }
template<class...Rest>
constexpr index<0> first_truth(std::true_type, Rest...) { return {}; }
template<class...Rest>
constexpr auto first_truth(std::false_type, Rest...rest) {
return index<first_truth( rest... )+1>{};
}
dispatch
接受一组编译时bool并返回一个lambda。这个lambda通过完美转发返回匹配第一个真正编译时bool的第一个元素:
template<class...Bools>
constexpr auto dispatch(Bools...bools) {
constexpr auto index = first_truth(bools...);
return [](auto&&...fs){
return std::get< decltype(index){} >(
std::forward_as_tuple( decltype(fs)(fs)... )
);
};
}
编译时bool类型:
template<bool b>
using bool_t = std::integral_constant<bool, b>;
template<bool b>
bool_t<b> bool_k{};
现在我们解决您的问题:
const char* const osName =
dispatch(
bool_k<os == OS::Linux>,
bool_k<os == OS::MacOs>,
bool_k<os == OS::MsWindows>
)(
"Linux",
"MacOS",
"MS Windows",
"Unknown"
);
应该接近编译时开关。我们可以通过更多的工作将bool与争论更紧密地结合起来。
代码未编译,可能包含tpyos。
答案 2 :(得分:1)
如何根据一些编译时间常量w / o模板特化来定义不同的类型?
这是:
class PokemonTableViewCell: UITableViewCell {
var pokemon: Pokemon!
@IBOutlet weak var pokemonImage: UIImageView!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var releaseDateLabel: UILabel!
@IBAction func setPokemon(sender: UIButton) {
currentPokemon = self.pokemon
}
}
正如您所看到的 - 我使用新构造constexpr auto osPropsCreate()
{
if constexpr (os == OS::Linux) {
struct Props { const char* name; int props1; using handle = int; };
return Props{"linux", 3};
} else if constexpr (os == OS::MacOs) {
struct Props { const char* name; using handle = float; };
return Props{"mac"};
} else if constexpr (os == OS::MsWindows) {
struct Props { const char* name; using handle = int; };
return Props{"win"};
} else
return;
}
using OsProps = decltype(osPropsCreate());
constexpr OsProps osProps = osPropsCreate();
从一些“实现”函数生成依赖于编译时常量的类型。在D语言中使用它并不像if constexpr
那么容易 - 但它有效 - 我可以这样做:
static if
接下来 - 根据编译时间常数定义不同的函数:
int linuxSpecific[osProps.props1];
int main() {
std::cout << osProps.name << std::endl;
OsProps::handle systemSpecificHandle;
}
实际上,它们可以是类似函数的对象(仿函数),也可以是嵌套类的静态成员函数。这没关系 - 人们可以完全自由地为不同的编译时常量定义不同的东西(在这种情况下是OS类型)。请注意,对于未知系统,我们只返回constexpr auto osGetNameCreate() {
if constexpr (os == OS::Linux) {
struct Definition {
static constexpr auto getName() {
return "linux";
}
};
return Definition::getName;
} else if constexpr (os == OS::MacOs) {
// we might use lambda as well
return [] { return "mac"; };
} else if constexpr (os == OS::MsWindows) {
struct Definition {
static constexpr auto getName() {
return "win";
}
};
} else
return;
}
constexpr auto osGetName = osGetNameCreate();
int main() {
std::cout << osGetName() << std::endl;
}
- 它将导致未知系统的编译错误...
回答第二个问题:
第一个答案在评论中提供了推理(link)。我的解释是C ++标准委员会还没有为这种变化做好准备。也许与D竞争将是/再次提出这个主题的一个很好的理由......