类成员函数参数列表是否可能依赖于模板参数?

问题描述 投票:1回答:2

是否可以编写模板化类,以便成员函数的签名/原型取决于模板参数?例如,如果我声明一个带有foo N个模板参数的类int,则成员函数foo::bar()的原型是否可能具有N个相同类型的参数?我知道我只是将std::arraystd::vector作为参数传递,但是在应用程序中,我认为这是行不通的。

template <int N>
class foo {
    std::array<float, N> _data;

 public:
    foo()  {};
    ~foo() {};

    void bar(float arg0, float arg1, float arg2 /*, ... */) const;
};
c++ class templates signature
2个回答
1
投票

自C ++ 17起,您拥有constexpr-if,而自C ++ 11起,您拥有static_assert。它们可以一起用来构建您想要的东西。我在这里没有考虑您的std::array,因为我不了解它的用法。这只是汇总传递给bar的参数:

template<size_t N, typename T = float>
class foo {
private:
    // recursive instantiation
    template<size_t Depth, typename... Args>
    T rebar(T f, Args... args) const {
        if constexpr(Depth>1) return f + rebar<Depth-1>(args...);
        else return f;
    }

public:
    template<typename... Args>
    T bar(Args... args) const {
        // killing all overloads with the wrong number of arguments
        static_assert(sizeof...(args)==N, "wrong number of arguments");

        return rebar<N>(args...);
    }
};

0
投票

但是,当然! (std::conjunction需要C ++ 17)

template<int N>
class foo {
public:    


    template<typename T, typename... Args,
        typename = std::enable_if_t<
            std::conjunction_v<
                std::is_same<T, Args>...
            >
            && sizeof...(Args) + 1 == N
        >
    >
    void bar(T first, Args... args) {
        std::cout << first;
        ((std::cout << ", " << args), ..., (std::cout << std::endl));
        //do stuff
    };
};

int main() {
    foo<3> f;
    f.bar(1,2,3);
    //prints 1, 2, 3
}

typename T声明函数的第一个参数的类型。

typename... Args声明包含一些自变量的参数包类型

[std::enable_if_t< val >如果val为真,则启用此功能,如果val为假,则禁用此功能

[std::conjunction要求传递给它的参数列表中的所有参数都为true才能返回true,aka std::conjunction<val1,val2,val3>大致等同于(val1 && val2 && val3),尽管所有val都必须是constexpr。

如果std::is_same<T,U>true是同一类型,则[T返回U

std::is_same<T,Args>...将参数包扩展为...std::is_same<T,Arg0>,std::is_same<T,Arg2>, etc..., std::is_same<T,ArgN>

因此,我们将其传递回去

[std::conjunction<std::is_same<T,Arg0>,std::is_same<T,Arg2>, etc..., std::is_same<T,ArgN>>,如果所有参数都相同,则评估为true

sizeof...(Args)返回Args中的参数数量因此,由于我们将T作为参数之一,因此sizeof...(Args) + 1返回传递给该函数的参数总数。

因此,如果该函数的参数总数为sizeof...(Args) + 1 == N,则true返回N

结合两者,

std::conjunction_v<std::is_same<T, Args>...> 
    && sizeof...(Args) + 1 == N

如果所有参数都是同一类型,并且总共有true个参数,将返回N

最后,如果通过将结果评估为std::enable_if_t,则启用了此功能

typename = std::enable_if_t<
    std::conjunction_v<
        std::is_same<T, Args>...
    >
    && sizeof...(Args) + 1 == N
>

将其包装起来,我们有

template<typename T, typename... Args,
    typename = std::enable_if_t<
        std::conjunction_v<
            std::is_same<T, Args>...
        >
        && sizeof...(Args) + 1 == N
    >e
>
void bar(T first, Args... args) {
    std::cout << first;
    ((std::cout << ", " << args), ..., (std::cout << std::endl));
    //do stuff
};

编辑:@Ted Lyngmo可能有更好的答案来使用static_assert,尤其是因为它提供了更好的错误消息。我将在此处发布此内容只是为了展示如何也具有“所有相同类型”的部分。

template<int N>
class foo {

    template<typename T, typename... Args>
    void barImpl(T first, Args... args) {
        //do stuff
    }
public: 

    template<typename T, typename... Args>
    void bar(T first, Args... args) {
        static_assert(std::conjunction_v<std::is_same<T, Args>...>, "all arguments must be the same type!");
        static_assert(sizeof...(Args) + 1 == N, "Must have exactly N arguments!");
        barImpl(std::forward<T>(first), std::forward<Args>(args)...);
    };
};
© www.soinside.com 2019 - 2024. All rights reserved.