Как написать этот статический метод constexpr для C ++ 17 в C ++ 11? - PullRequest
0 голосов
/ 02 января 2019

Какой самый краткий способ написать приведенный ниже метод static constexpr size_t Foo<>::sum() в C ++ 11?Это прекрасно работает с компиляторами C ++ 17, но я ищу способ, который работает на g ++, clang и Visual Studio 2015 в режиме C ++ 11.

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

Ответы [ 4 ]

0 голосов
/ 02 января 2019

Альтернативный подход к Ответ HolyBlackCat заключается в использовании перегрузки функций:

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_constant с вместо size_t, что имеет некоторые преимущества.

0 голосов
/ 02 января 2019

С диспетчеризацией тегов:

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>{} );
}
0 голосов
/ 02 января 2019

Как насчет специализации полного класса 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;
}

- РЕДАКТИРОВАТЬ -

ОП спрос:

На практике Foo - это большой класс со многими функциями-членами.Требует ли это повторного определения их всех?

Не обязательно: вы можете сделать трюк с базовым классом / структурой Foo (Bar, в следующем примере)

#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;
}
0 голосов
/ 02 января 2019

Я бы написал это следующим образом:

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;
}
...