类似于"如果constexpr"但是对于课程定义

时间:2016-12-13 10:30:44

标签: c++ class c-preprocessor c++17 if-constexpr

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 ++?

3 个答案:

答案 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竞争将是/再次提出这个主题的一个很好的理由......