【问题标题】:Defining a static constexpr member by call to constexpr function通过调用 constexpr 函数定义静态 constexpr 成员
【发布时间】:2017-06-27 09:59:47
【问题描述】:

我的问题如下。我想根据 constexpr 值列表对类型列表进行排序。问题可以归结为这个函数:

template <typename U, typename V>
auto min(U,V) -> std::conditional_t<U::value < V::value, U, V>
{ return {}; }

而 value 必须分别是每种类型的某个静态 constexpr 成员。 下面的 sn -p 演示了用法:

// (I)

// This must even be declared outside of a function body due to the statics :(
struct X { static constexpr double value = 2.; };
struct Y { static constexpr double value = 1.; };

int main()
{
    X x;
    Y y;
    auto z = min(x,y);
    std::cout << typeid(z).name() << " : " << z.value << std::endl;
}

我的目标是在调用函数时提供值。我最接近这个目标的是 以下

template <double (*F)()>
struct Value { static constexpr double value = F(); };

可以像这样使用 lambdas 调用:

// (II)
auto w = min(Value<[]{ return 3.14; }>{}, Value<[]{ return 2.71; }>{});
std::cout << typeid(w).name() << " : " << w.value << std::endl;

要排序的实际类型可以是附加参数。

问题是根据标准,以上不是有效的C++。但是,最新的 clang 确实可以编译 这很优雅。

现在,我的问题是:是否有另一种符合标准的方式来实现上述(清单(II)),即定义一个函数 根据就地(以某种方式)作为函数参数提供的 constexor 对象计算类型?


P.S.:我知道使用 std::integral_constant 的解决方案。但是,这仅限于整数类型。我对适用于所有 constexpr 对象(尤其是浮点类型和字符串)的解决方案感兴趣。

【问题讨论】:

  • 出于好奇:为什么这不符合标准?
  • @KjMag 见this question

标签: c++ templates language-lawyer template-meta-programming c++17


【解决方案1】:

编辑:

要处理浮点值以及整数类型场景,您可以使用用户定义的文字模板,例如:

#include <type_traits>
#include <utility>
#include <typeinfo>
#include <iostream>

template <class FloatingPointType, class... Cs>
constexpr FloatingPointType char_list_to_(Cs... cs) {
    char arr[] = {cs...};
    FloatingPointType lhs = 0;
    bool contains_dot = false;
    for (std::size_t i = 0; i < sizeof...(Cs) && !(contains_dot |= (arr[i] == '.')); i++) { 
        lhs *= 10;
        lhs += arr[i] - '0';
    }
    FloatingPointType rhs = 0;
    for (int i = sizeof...(Cs) - 1; i > 0 && arr[i] != '.'; i--) {
       rhs /= 10;
       rhs += arr[i] - '0';
    }
    rhs /= 10;
    return (contains_dot)?lhs+rhs:lhs;
}

template <class FloatingPointType, char... Cs>
struct FloatingPointValue {

    static constexpr FloatingPointType value = char_list_to_<FloatingPointType>(Cs...);

    constexpr operator FloatingPointType() {
        return value;
    }
};

template <class FloatingPointType, char... Cs>
constexpr FloatingPointType FloatingPointValue<FloatingPointType, Cs...>::value;

template <char... Cs>
FloatingPointValue<double, Cs...> operator""_fv() {
    return {};
}


template <typename U, typename V>
auto min(U,V) -> std::conditional_t<(U{}<V{}), U, V>
{ return {}; }

int main() {
   auto w = min(3.14_fv, 2.71_fv);
   std::cout << typeid(w).name() << " : " << w.value << std::endl;
}

输出:

18FloatingPointValueIdJLc50ELc46ELc55ELc49EEE : 2.71

c++filt -t 18FloatingPointValueIdJLc50ELc46ELc55ELc49EEE的输出:

FloatingPointValue<double, (char)50, (char)46, (char)55, (char)49>

[live demo]


但是,如果您希望将其应用于字符串文字,则目前缺乏对由 c++ 标准引起的功能的支持。但是,如果您能够接受不太便携的选项,那么 clang 和 gcc 支持 gnu 扩展:

#include <type_traits>
#include <utility>
#include <typeinfo>
#include <iostream>

template <class CharT, CharT... Cs>
struct Value {

    static constexpr std::size_t size = sizeof...(Cs);
    static constexpr CharT const value[sizeof...(Cs) + 1] = {Cs..., '\0'};

    template <class RHS>
    constexpr bool operator<(RHS) {
        for (std::size_t i = 0; i < size && i < RHS::size; i++) {
            if (value[i] != RHS::value[i]) {
                return value[i] < RHS::value[i];
            }
        }
        return size < RHS::size;
    }
};

template <class CharT, CharT... Cs>
constexpr CharT const Value<CharT, Cs...>::value[sizeof...(Cs) + 1];

template <class CharT, CharT... Cs>
Value<CharT, Cs...> operator""_v() {
    return {};
}


template <typename U, typename V>
auto min(U,V) -> std::conditional_t<(U{}<V{}), U, V>
{ return {}; }

int main() {
   auto w = min("cde"_v, "abc"_v);
   std::cout << typeid(w).name() << " : " << w.value << std::endl;
}

输出:

5ValueIcJLc97ELc98ELc99EEE : abc

c++filt -t 5ValueIcJLc97ELc98ELc99EEE 的输出:

Value<char, (char)97, (char)98, (char)99>

[live demo]

【讨论】:

  • 这确实是一个很好的字符串解决方案。不幸的是,它仅限于 gcc,我不知道如何将其扩展到浮点值。在这里,字符串作为字符的可变参数模板列表传递,但我不能为浮点执行此操作。我最终会遇到我试图解决的同样情况——将浮动隐藏在某个静态成员中。
  • @AndréBergner 是的,您需要将浮点存储在某个静态成员中,但您可以内联创建它,例如通过电话:max("3.14"_vf, "2.71"_vf)。努力归结为重载 value 类型的 less 运算符。 PS。 clang 也支持它......并且有标准化它的建议。不幸的是,你不会在 c++17 中得到它,但也许是 c++20……谁知道呢?!我正在祈祷它:)
  • @AndréBergner 实际上事实证明,已经有标准化的用户定义的数值文字模板(参见例如here)这将使该方法更加便携,例如max(3.14_vf, 2.71_vf)
  • 我完全忘记了那个。是的,这解决了浮点情况。我该怎么做才能让clang编译字符串一?我需要一个特殊的命令行开关吗?不适用于我最新的 clang。仍然可以很好地解决任意 constexpr 类型的一般情况。
  • @AndréBergner 不确定。 wandbox 上最新的 clang 在这里没有任何问题。哪个版本导致问题?
猜你喜欢
  • 2019-07-05
  • 2012-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-12
  • 1970-01-01
  • 2020-03-10
  • 1970-01-01
相关资源
最近更新 更多