简体   繁体   中英

Avoid colliding members in C++ templates

I have a class

template <typename T, typename W>
class A {
    void foo(W);
    void foo(T);
    void foo(int);
}

When T=int , W=int , or W=T , this class fails to compile. How can I get the methods to take priority over each other?

I want the priority W > T > int . So if W=T , foo(T) is ignored and foo(W) is called. If T=int , foo(int) is ignored and foo(T) is called.

The compiler is VS2012, but I have Linux too, and will consider GCC/Clang solutions as well. Anything that compiles on any mainstream compiler goes, but only if you say what compilers it works on.

I would tag dispatch. Override dispatching is easy to understand and scales.

We start with a perfect forwarder:

template<class U> void foo(U&&u){
  foo( std::forward<U>(u), std::is_convertible<U, W>{}, std::is_convertible<U,T>{} );
}

it creates tag types, in this case true or false types, to dispatch on.

This one:

void foo( W, std::true_type, ... );

catches everything that can convert to W.

Next, we block this one:

void foo( T, std::false_type, std::true_type );

from considerimg cases where the first argument can convert to W .

Finally, this one:

void foo( int, std::false_type, std::false_type );

can only be considered if the first parameter cannot convert to either.

Fancier tag types, or doing the dispatching one at a time, are both possible.

Sorry for typos.

I use a single C++11 feature -- {} to construct an object -- above. If your compiler lacks support for that C++11 feature, simply upgrade your compiler, it is 2014, get with it. Failing that, replace {} with () .

Use std::enable_if :

#include <type_traits>

template <typename T, typename W>
struct A {
    void foo(W) {}
    template<typename XT=T> typename std::enable_if<std::is_same<XT,T>::value
      && !std::is_same<T, W>::value, void>::type foo(T) {}
    template<typename XT=int> typename std::enable_if<std::is_same<XT,int>::value
      && !std::is_same<int, T>::value
      && !std::is_same<int, W>::value, void>::type foo(int) {}
};

Added for testing:

template struct A<short,char>;
template struct A<char,char>;
template struct A<char,int>;
template struct A<int,char>;
template struct A<int, int>;

struct S {};

int main() {
    A<S, int>{}.foo(S{});
}

For the relevant part of your template, you could use speclializations:

template <typename U, typename W>
struct Foo
{
    void f(U);
    void f(W);
};

template <typename T>
struct Foo<T, T>
{
    void f(T);
};

For the rest of your class or class template, you can inherit from Foo<A, B> so you can keep the common code out of the part that needs to be specialized:

template <typename A, typename B>
struct TheClass : Foo<A, B>
{
    // common code
};

Try template specializations:

template <typename T, typename W>
class A {
    void foo(W);
    void foo(T);
    void foo(int);
};

template <typename T>
class A<T, T> {
    void foo(T);
    void foo(int);
};
template <>
class A<int, int> {
    void foo(int);
};

Here is a solution without specializations of A, but with two helper structures in a few forms.

#include <iostream>

template<typename T, typename W>
struct T_type { typedef T type; };
template<typename W>
struct T_type<W, W>  { typedef void* type; /*dummy type*/};

template<typename T, typename W>
struct int_type { typedef int type; };
template<typename W>
struct int_type<int, W> { typedef void** type; /*dummy type*/};
template<typename T>
struct int_type<T, int> { typedef void** type; /*dummy type*/};
template<>
struct int_type<int, int> { typedef void** type; /*dummy type*/};

template<typename T, typename W>
class A {
public:
    void foo(W w) {
        std::cout << "foo(W)" << std::endl;
    }
    void foo(typename T_type<T, W>::type t) {
        std::cout << "foo(T)" << std::endl;
    }
    void foo(typename int_type<T, W>::type i) {
        std::cout << "foo(int)" << std::endl;
    }
};

int main() {
    std::cout << "A<float, char>" << std::endl;
    A<float, char> a;
    a.foo(1.0f);
    a.foo('1');
    a.foo(1);

    std::cout << "A<float, float>" << std::endl;
    A<float, float> b;
    b.foo(1.0f);
    b.foo(1);

    std::cout << "A<int, int>" << std::endl;
    A<int, int> c;
    c.foo(1);

    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM