类似于 "if constexpr" 但对于 class 定义
Something like "if constexpr" but for class definition
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 定义中使用它以不同方式定义 class 的某些成员:
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 也不适用于非 class-范围(如全局范围):
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 中的 class 和全局作用域?我不是在这里要求对模板专业化的另一种解释......而是与 if constexpr
...
具有相似简单性的东西
Q2是否有计划为上述范围扩展 C++?
How to achieve the similar effect like if-constexpr in functions - for class and global scope in C++1z/C++14? And I am not asking here for yet another explanation of template specialization...
你基本上只是说,"I want template specialization, but without all that pesky template specialization."
if constexpr
是根据编译时结构改变函数行为的工具。模板专业化是 C++ 提供的工具,用于根据编译时构造进行 定义 更改。它是 C++ 为该功能提供的唯一工具。
现在对于初始化变量的简单情况,您始终可以创建和调用 lambda。 C++17 提供 constexpr
对 lambda 的支持,并且 lambda 可以使用 if constexpr
来决定 return.
的值
Are there any plan to extend C++ for the above mentioned scopes?
没有。 Here are all of the proposals,以及过去几年的 none 深入研究了这个领域。
而且他们永远不会这样做的可能性很小。
索引类型:
template<std::size_t I>
using index = std::integral_constant<std::size_t, I>;
first_truth
采用一组编译时布尔值并说明第一个布尔值在编译时的索引是什么。如果你传递给它 N 个编译时布尔值,它 returns N if all are false:
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
采用一组编译时布尔值和 returns 一个 lambda。这个 lambda returns 通过完美转发匹配第一个真实编译时间 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)... )
);
};
}
编译时布尔类型:
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"
);
这应该近似于编译时开关。我们可以通过更多的工作将布尔值与参数更紧密地联系起来。
代码未编译,可能包含 tpyos。
how to define different types based on some compile time constant w/o template specialization?
这里是:
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();
如您所见 - 我使用新结构 if constexpr
从一些“实现”函数生成依赖于编译时间常量的类型。它不像 D 语言中的 static if
那样容易使用 - 但它有效 - 我可以做到:
int linuxSpecific[osProps.props1];
int main() {
std::cout << osProps.name << std::endl;
OsProps::handle systemSpecificHandle;
}
Next thing - define different functions depending on compile time constant:
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";
}
};
return Definition::getName;
} else
return;
}
constexpr auto osGetName = osGetNameCreate();
int main() {
std::cout << osGetName() << std::endl;
}
实际上,它们可以是类函数对象(仿函数)或嵌套的静态成员函数 类。这无关紧要——人们可以完全自由地为不同的编译时间常量定义不同的东西(在本例中为 OS 类型)。请注意,对于未知系统,我们只是 return void
- 这将导致未知系统的编译错误...
Answering to second question:
第一个答案在评论中提供推理(link)。我的解释是 C++ 标准委员会还没有准备好进行这种更改。也许与 D will/would 竞争是再次提出这个话题的一个很好的理由......
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 定义中使用它以不同方式定义 class 的某些成员:
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 也不适用于非 class-范围(如全局范围):
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 中的 class 和全局作用域?我不是在这里要求对模板专业化的另一种解释......而是与 if constexpr
...
Q2是否有计划为上述范围扩展 C++?
How to achieve the similar effect like if-constexpr in functions - for class and global scope in C++1z/C++14? And I am not asking here for yet another explanation of template specialization...
你基本上只是说,"I want template specialization, but without all that pesky template specialization."
if constexpr
是根据编译时结构改变函数行为的工具。模板专业化是 C++ 提供的工具,用于根据编译时构造进行 定义 更改。它是 C++ 为该功能提供的唯一工具。
现在对于初始化变量的简单情况,您始终可以创建和调用 lambda。 C++17 提供 constexpr
对 lambda 的支持,并且 lambda 可以使用 if constexpr
来决定 return.
Are there any plan to extend C++ for the above mentioned scopes?
没有。 Here are all of the proposals,以及过去几年的 none 深入研究了这个领域。
而且他们永远不会这样做的可能性很小。
索引类型:
template<std::size_t I>
using index = std::integral_constant<std::size_t, I>;
first_truth
采用一组编译时布尔值并说明第一个布尔值在编译时的索引是什么。如果你传递给它 N 个编译时布尔值,它 returns N if all are false:
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
采用一组编译时布尔值和 returns 一个 lambda。这个 lambda returns 通过完美转发匹配第一个真实编译时间 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)... )
);
};
}
编译时布尔类型:
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"
);
这应该近似于编译时开关。我们可以通过更多的工作将布尔值与参数更紧密地联系起来。
代码未编译,可能包含 tpyos。
how to define different types based on some compile time constant w/o template specialization?
这里是:
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();
如您所见 - 我使用新结构 if constexpr
从一些“实现”函数生成依赖于编译时间常量的类型。它不像 D 语言中的 static if
那样容易使用 - 但它有效 - 我可以做到:
int linuxSpecific[osProps.props1];
int main() {
std::cout << osProps.name << std::endl;
OsProps::handle systemSpecificHandle;
}
Next thing - define different functions depending on compile time constant:
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";
}
};
return Definition::getName;
} else
return;
}
constexpr auto osGetName = osGetNameCreate();
int main() {
std::cout << osGetName() << std::endl;
}
实际上,它们可以是类函数对象(仿函数)或嵌套的静态成员函数 类。这无关紧要——人们可以完全自由地为不同的编译时间常量定义不同的东西(在本例中为 OS 类型)。请注意,对于未知系统,我们只是 return void
- 这将导致未知系统的编译错误...
Answering to second question:
第一个答案在评论中提供推理(link)。我的解释是 C++ 标准委员会还没有准备好进行这种更改。也许与 D will/would 竞争是再次提出这个话题的一个很好的理由......