【问题标题】:How to write this C++17 static constexpr method in C++11?如何在 C++11 中编写这个 C++17 静态 constexpr 方法?
【发布时间】:2019-01-01 21:08:54
【问题描述】:

在 C++11 中编写下面的static constexpr size_t Foo<>::sum() 方法最简洁的方法是什么?这适用于 C++17 编译器,但我正在寻找一种在 C++11 模式下适用于 g++、clang 和 Visual Studio 2015 的方法。

#include <iostream>
#include <type_traits>

template<typename T,size_t N>
class Foo
{
  public:
    static constexpr size_t sum();
};

template<typename>
struct is_foo : std::false_type { };
template<typename T,size_t N>
struct is_foo<Foo<T,N>> : std::true_type { };

template<typename T,size_t N>
constexpr size_t Foo<T,N>::sum()
{
    if constexpr (is_foo<T>::value)
        return N + T::sum();
    else
        return N;
}

int main()
{
    constexpr size_t sum = Foo<Foo<Foo<double,3>,4>,5>::sum(); // 12 = 3+4+5
    std::cout << "sum = " << sum << std::endl;
    return 0;
}

编译运行:

$ g++ --version
g++ (GCC) 7.3.1 20180303 (Red Hat 7.3.1-5)
...
$ g++ -std=c++17 sum.cpp
$ ./a.out 
sum = 12

我可以为sum() 编写一个外部函子来完成此任务,但我真的希望它是上面的static constexpr 成员函数。这在 C++11 中是否可行?

【问题讨论】:

  • 你只是在问如何替换if constexpr,对吧? enable_if?
  • 我尝试使用 constexpr typename std::enable_if&lt;is_foo&lt;T&gt;::value,size_t&gt;::type Foo&lt;T,N&gt;::sum() 进行 SFINAE,但收到错误 prototype for 'constexpr typename std::enable_if&lt;is_foo&lt; &lt;template-parameter-1-1&gt; &gt;::value, long unsigned int&gt;::type Foo&lt;T, N&gt;::sum()' does not match any in class 'Foo&lt;T, N&gt;'

标签: c++ c++11 templates c++17 template-meta-programming


【解决方案1】:

我会这样写:

template <typename>
struct foo_sum : std::integral_constant<size_t, 0> {};

template <typename T, size_t N>
struct foo_sum<Foo<T, N>> : std::integral_constant<size_t, foo_sum<T>::value + N> {};

它可以很容易地包装成static constexpr 函数:

template <typename T, size_t N>
constexpr size_t Foo<T, N>::sum()
{
    return foo_sum<Foo<T, N>>::value;
}

【讨论】:

  • 这确实有效。在 Visual Studio 2015 上,我遇到了臭名昭著的 C1001 致命错误,但通过改组一些周围的代码,它编译了。从来没有遇到过 gcc 或 clang 的问题。谢谢!
【解决方案2】:

HolyBlackCat's answer 的另一种方法是使用函数重载:

template <typename> struct tag {};

template <typename T>
constexpr size_t sum(tag<T>) {
    return 0;
}

template <typename T, size_t N>
constexpr size_t sum(tag<Foo<T,N>>) {
    return sum(tag<T>{}) + N;
}

眼睛可能会轻松一些。还可以让函数返回integral_constants 而不是size_t,这有一些好处。

【讨论】:

    【解决方案3】:

    使用标签调度:

    template<typename T,size_t N>
    class Foo
    {
        static constexpr size_t sum_impl(std::true_type) { return N + T::sum(); }
        static constexpr size_t sum_impl(std::false_type) { return N; };
      public:
        static constexpr size_t sum();
    };
    

    sum的实现:

    template<typename T,size_t N>
    constexpr size_t Foo<T,N>::sum()
    {
        return sum_impl( is_foo<T>{} );
    }
    

    【讨论】:

    • 我已经尝试过这种方法,它确实适用于 g++ 和 clang。出于某种原因,Visual Studio 2015 仍然尝试编译 sum_impl(std::true_type),并在 T=double 时导致编译器错误。
    • @Matt Shame。 This example 似乎适用于较新的 MSVC。
    • @Matt 尝试将第一个 sum_impl 本身作为模板。
    【解决方案4】:

    专攻完整的Foo 课程怎么样?

    你也可以避开is_foo

    #include <iostream>
    #include <type_traits>
    
    template <typename, std::size_t N>
    struct Foo
     { static constexpr std::size_t sum () { return N; } };
    
    template <typename T, std::size_t N1, std::size_t N2>
    struct Foo<Foo<T, N1>, N2>
     { static constexpr std::size_t sum () { return Foo<T, N1>::sum() + N2; } };
    
    int main()
    {
        constexpr size_t sum = Foo<Foo<Foo<double,3>,4>,5>::sum(); // 12 = 3+4+5
        std::cout << "sum = " << sum << std::endl;
        return 0;
    }
    

    -- 编辑--

    OP 问:

    实际上,Foo 是一个包含许多成员函数的大类。这需要重新声明所有这些吗?

    不一定:您可以使用FooBar,在以下示例中)的基类/结构来实现这一点

    #include <iostream>
    #include <type_traits>
    
    template <typename T, std::size_t N>
    struct Foo;
    
    template <typename, std::size_t N>
    struct Bar
     { static constexpr std::size_t sum () { return N; } };
    
    template <typename T, std::size_t N1, std::size_t N2>
    struct Bar<Foo<T, N1>, N2>
     { static constexpr std::size_t sum () { return Foo<T, N1>::sum() + N2; } };
    
    template <typename T, std::size_t N>
    struct Foo : public Bar<T, N>
     { /* many common member and methods */};
    
    int main()
    {
        constexpr size_t sum = Foo<Foo<Foo<double,3>,4>,5>::sum(); // 12 = 3+4+5
        std::cout << "sum = " << sum << std::endl;
        return 0;
    }
    

    【讨论】:

    • 实际上,Foo 是一个有很多成员函数的大类。这需要重新声明所有这些吗?
    • @Matt - 答案改进;希望这会有所帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-10
    • 1970-01-01
    • 1970-01-01
    • 2017-12-24
    • 1970-01-01
    相关资源
    最近更新 更多