Специализировать функции-члены в зависимости от размера контейнера-члена - PullRequest
0 голосов
/ 15 февраля 2019

У меня есть класс, который содержит несколько контейнеров статического размера:

template <typename Container>
struct Point {
    Container container;
    ... 
    void bar();
}

, где класс Container может выглядеть следующим образом:

struct Container1 {
    static constexpr size_t size = 5;
}

Теперь я хочу специализироваться на bar метод, основанный на размере контейнера.Я не понимаю, как это сделать.

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

Я хотел бы решение C ++ 11.C ++ 14 может работать, но компиляторы, с которыми мы работаем, обычно имеют пятнистую поддержку C ++ 14.

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

Стек Дэнни предложил решение, которое компилируется с Clang, но не с GCC.

Ответы [ 3 ]

0 голосов
/ 15 февраля 2019

Вы можете применить перегрузку шаблона с помощью SFINAE .

template <typename C = Container>
auto bar() -> typename std::enable_if<C::size==5>::type { ... }
template <typename C = Container>
auto bar() -> typename std::enable_if<C::size==42>::type { ... }
... ...

LIVE

0 голосов
/ 15 февраля 2019

Вместо специализации используйте SFINAE

template <typename Container>
class Point {
    Container container;
    template<size_t S> std::enable_if_t<S==3>
    bar_t() { std::cout << "specialisation for size=3\n"; }
    template<size_t S> std::enable_if_t<S==5>
    bar_t() { std::cout << "specialisation for size=5\n"; }
    template<size_t S> std::enable_if_t<S==42>
    bar_t() { std::cout << "specialisation for size=42\n"; }
  public:
    void bar()
    { bar_t<Container::size>(); }
};

std::enable_if_t - это C ++ 14, но вы можете тривиально объявить это сами:

#if __cplusplus < 201402L
template<bool C, typename T=void>
using enable_if_t = typename enable_if<C,T>::type;
#endif

Кстати, ваш вопрос пахнет как XY проблема : вам действительно нужно специализировать bar() для Container::size?В следующем примере цикл развернут для любого размера N.

template<typename scalar, size_t N>
class point // a point in R^N
{
    scalar array[N];
  public:
    // multiplication with scalar
    point& operator*=(scalar x) noexcept
    {
        // unroll loop using template meta programming
        loop([array,x](size_t i) { array[i] *= x; };);
        /* alternatively: rely on the compiler to do it
        for(size_t i=0; i!=N; ++i)
            array[i] *= x;
        */
        return *this;   
    }
  private:
    template<size_t I=0, typename Lambda>
    static enable_if_t<(I<N)> loop(Lambda &&lambda)            
    {
        lambda(I);
        loop<I+1>(lambda);
    }
    template<size_t I=0, typename Lambda>
    static enable_if_t<(I>=N)> loop(Lambda &&)         {}
};
0 голосов
/ 15 февраля 2019

Это называется специализацией шаблона и работает следующим образом:

#include <cstdint>
#include <iostream>

struct Container1 {
    static constexpr size_t size = 5;
};
struct Container2 {
    static constexpr size_t size = 3;
};


template <typename Container>
struct Point {
    Container container;

    void bar() {
        this->bar_templated<Container::size>();
    }

private:
    template<std::size_t size>
    void bar_templated() {
        std::cout << "default\n";
    }
    template<>
    void bar_templated<3>() {
        std::cout << "specialized <3>\n";
    }
    template<>
    void bar_templated<5>() {
        std::cout << "specialized <5>\n";
    }
};

int main(){
    Point<Container1> p1;
    p1.bar();
    Point<Container2> p2;
    p2.bar();
}

вывод

specialized <5>
specialized <3>

из-за ошибка 85282 в gcc* * * * * * * * * * * * * * * * * * * (спасибо @songyuanyao):

25: 14: ошибка: явная специализация в не-пространстве имен: невозможно скомпилировать явную специализациюобласть видимости "struct Point"

26: 27: ошибка: идентификатор шаблона 'bar_templated <3>' в объявлении основного шаблона

...

30: 10:ошибка: 'void Point :: bar_templated ()' не может быть перегружен

Но вы можете обойти это, переместив функции из класса и все же достигнув специализации:

template<std::size_t size>
void bar_templated() {
    std::cout << "default\n";
}
template<>
void bar_templated<3>() {
    std::cout << "specialized 3\n";
}
template<>
void bar_templated<5>() {
    std::cout << "specialized 5\n";
}


template <typename Container>
struct Point {
    Container container;

    void bar() {
        bar_templated<Container::size>();
    }
};

Таким образом, функции являются общедоступными, что может не соответствовать вашим ожиданиям.Что ж, если вы пишете в заголовочном файле, вы можете определить их в анонимном пространстве имен.


Также: , если constexpr - но это только C ++ 17 и вперед.Это уменьшает размер кода на много и сохраняет его логическую природу, что делает его лучшим подходом здесь, конечно.

void bar() {
    if constexpr (Container::size == 3) {
        std::cout << "specialized <3>\n";
    }
    else if constexpr (Container::size == 5) {
        std::cout << "specialized <5>\n";
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...