根据成员容器的大小专门化成员函数

时间:2019-02-15 09:01:33

标签: c++ c++11 templates template-specialization

我有一个包含一些静态大小的容器的类:

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支持。

编辑:

Stack Danny建议使用Clang而不是GCC进行编译的解决方案。

3 个答案:

答案 0 :(得分:5)

使用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 problem:您真的需要对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 &&)         {}
};

答案 1 :(得分:4)

这称为模板专门化,其工作方式如下:

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

由于bug 85282的gcc使其impossible to compile an explicit specialization in non-namespace scope(感谢@songyuanyao),出现了以下错误:

  

25:14:错误:非命名空间范围“结构点”中的显式专业化

     

26:27:错误:主模板的声明中的template-id'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>();
    }
};

通过这种方式,这些功能是公共的,但是可能不是您想要的。好吧,如果要在头文件中编写,则可以在匿名名称空间中定义它们。


也:if 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";
    }
}

答案 2 :(得分:2)

您可以使用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