模板参数不能满足所有要求

时间:2016-08-05 17:32:21

标签: c++ templates c++11 c++14

这是可能的:

struct A {
    //void f();  < not declared in struct A
};

template<typename T>
struct Wrapper {
    T t;
    void call_f() { t.f(); }
};

int main() {
    Wrapper<A> w;
}

只要w.call_f()未被调用,此编译就可以了。无法实例化w.call_f(),因为A::f不存在。

我遇到过这种包装模板的情况,该模板可以与不同的T类型一起使用,这些类型并不总是实现接口的所有部分。 (主要是为了避免代码重复)。

这不起作用:

struct A {
    //using i_type = int;   < not declared/defined
};

template<typename T>
struct Wrapper {
    using its_i_type = typename T::i_type;
       // compile error, even if `i_type` gets never used
};

int main() {
    Wrapper<A> w;
}

这两个都没有:

struct A {
    //using i_type = int;   < not declared/defined
};

template<typename T>
struct Wrapper {
    typename T::i_type call_f() { return 0; }
       // does not compile, even if `call_f()` is never instantiated
};

int main() {
    Wrapper<A> w;
}

有没有一种很好的方法来处理这些情况,没有大量的代码重复(比如Wrapper的专业化等)?

2 个答案:

答案 0 :(得分:3)

您可以推迟its_i_type的类型扣除。基本上,您创建一个必须经历的简单包装器。

要将其扩展到您需要的其他类型,(我想建议type_traits - 就像解决方案一样,但由于您不需要专业化),您可以定义所需的所有类型:

template<typename T>
struct Wrapper {
private:
    template<typename U> struct i_typper { using type = typename U::i_type; };
    template<typename U> struct k_typper { using type = typename U::k_type; };
    template<typename U> struct p_typper { using type = typename U::p_type; };
public:
    using i_trait = i_typper<T>;
    using k_trait = k_typper<T>;
    using p_trait = p_typper<T>;
};

示例:

struct A { using i_type = int; };
struct B { using i_type = int;    using k_type = float; };

int main() {
    Wrapper<A> w;   //Works now.

    Wrapper<A>::i_trait::type  mk1;  //Works
    Wrapper<A>::k_trait::type  mk2;  //Fails, not defined
    Wrapper<B>::i_trait::type  mk3;  //Works
    Wrapper<B>::k_trait::type  mk4;  //Works
}

对于以下情况:

template<typename T>
struct Wrapper {
    typename T::i_type call_f() { return 0; }
       // does not compile, even if `call_f()` is never instantiated
};

你在这里几乎没有选择:

  1. 使该功能成为会员功能模板
  2. 使用某种形式的type_traits机制,这仍将涉及专业化
  3. 在基类Wrapper中抽象普通WrapperBase内容的方式;
  4. 对于第一个选项,您需要稍微修改一下以进一步defer deduction

    template<typename T>
    struct Wrapper {
    private:
        template<typename U, typename> struct i_typper { using type = typename U::i_type; };
        template<typename U, typename> struct k_typper { using type = typename U::k_type; };
        template<typename U, typename> struct p_typper { using type = typename U::p_type; };
    public:
        using i_trait = i_typper<T, void>;
        using k_trait = k_typper<T, void>;
        using p_trait = p_typper<T, void>;
    
        template<typename U = void>
        typename k_typper<T, U>::type call_f() { return 0; }
    };
    

    我会将第二个选项留作练习:(最终可能会出现类似的情况:

    template<typename T>
    struct wrapper_traits {
        ....
    };
    
    template<>
    struct wrapper_traits<A>{
       using ....
    };
    
    template<typename T>
    struct Wrapper {
       ....
    public:
        using i_trait = wrapper_traits<T>;
        using k_trait = wrapper_traits<T>;
        using p_trait = wrapper_traits<T>;
    };
    

    Jarod's answer更简单。但是,如果您无权访问std::experimental,或者您的公司代码政策禁止您这样做,这将有效...

答案 1 :(得分:2)

使用std::experimental::is_detected,您可以

template<typename T>
using i_type_t = typename T::i_type;

template<typename T>
struct Wrapper {
    using its_i_type = typename std::experimental::detected_t<i_type_t, T>;
       // would be T::i_type or std::experimental::nonesuch
};

或者更好地处理案例,例如:

template<typename T, bool = std::experimental::is_detected<i_type_t, T>::value>
struct WrapperWithIType {
    // Empty for false case.
};

template<typename T>
struct WrapperWithIType<T, true> {
    using its_i_type = i_type_t<T>;

    its_i_type call_f() { return 0; }
};

然后

template<typename T>
struct Wrapper : WrapperWithIType<T> {
    // Common stuff
};