is there any way to partial instance a c++ template

Viewed 80

I have a template for some type, and some code in the template are not valid for all types, so I want to skip them when needed.

struct T1
{
    int getData(){return 1;}
};

struct T2
{
    string getData(){return "string";}
};

struct T3
{
    // no getData()
};

template<typename T>
void printData(T param)
{
    cout << param.getData() << endl;
}

int main() {

    T1 t1;
    T2 t2;
    T3 t3;

    printData(t1);
    printData(t2);
    printData(t3);  // fails for T3 has no getData
}

I want to know is there some way like this(pseudo code):

template<typename T>
void printData(T param)
{
    if(T != T3) // compile time check, instance on this condition
    {
        cout << param.getData() << endl;
    }
}

I have tried std::is_same but it did not work

2 Answers

Starting from C++17, you can use if constexpr (!std::is_same_v<T, T3>), as suggested in songyuanyao's answer.

In C++11/C++14 I propose two ways.

(1) tag dispatching

template <typename T>
void printData (T param, std::true_type)
 { }

template <typename T>
void printData (T param, std::false_type)
 { std::cout << param.getData() << std::endl; }

template <typename T>
void printData (T param)
 { printData(param, std::is_same<T, T3>{}); }

(2) SFINAE

// ever enabled
template <typename T>
void printData (T param, long)
 { }

// preferred (int instead of long) but enabled only
// when param support getData()
template <typename T>
auto printData (T param, int)
   -> decltype( param.getData(), void() )
 { std::cout << param.getData() << std::endl; }

template <typename T>
void printData (T param)
 { printData(param, 0); }

The problem is that the statement_true will still be evaluated in compilation when T is T3, which leads to compilation error.

You could use Constexpr If (since C++17).

If the value is true, then statement-false is discarded (if present), otherwise, statement-true is discarded.

e.g.

if constexpr (!std::is_same_v<T, T3>)
{
    cout << param.getData() << endl;
}
Related