Conditional compilation based on template values?

2020-02-01 08:26发布

the question posed in: Type condition in template

is very similar, yet the original question wasn't quite answered.

#include "stdafx.h"
#include <type_traits>


class AA {
public:
    double a;

    double Plus(AA &b) {
        return a + b.a;
    }
};

template<class T> double doit(T &t) {
    if (std::is_same<T, AA>::value)
        return t.Plus(t);
    else
        return t + t;
}

int _tmain(int argc, _TCHAR* argv[])
{
    double a;
    AA aa;

    doit(a);
    doit(aa);

    return 0;
}

This doesn't compile, nor did I expect it to. Is something like this possible? Being, based on the template value, I want some code to be compiled, and others not. Here, 'double' doesn't have a method called "Plus" and class "AA" doesn't override the '+' operator. Operator overloading isn't always desirable when considering subtle semantics to the operations, so I'm looking for an alternative. I'd prefer to do #ifdef's (truly conditional compilation as posed in the ref'd question), but based on template values.

标签: c++ templates
5条回答
Bombasti
2楼-- · 2020-02-01 08:47

This code compiles and runs:

#include <boost/type_traits/is_floating_point.hpp>
#include <boost/type_traits/is_class.hpp>
#include <boost/utility/enable_if.hpp>

using namespace boost;

class AA {
public:
    double a;

    AA Plus(AA &b) {
        AA _a;
        _a.a = a + b.a;
        return _a;
    }
};


template<class T>
typename disable_if<is_floating_point<T>, T >::type
doit(T &t) {
    return t.Plus(t);
}


template<class T>
typename enable_if<is_floating_point<T>, T >::type
doit(T &t) {
    return t+t;
}

int _tmain(int argc, _TCHAR* argv[])
{
    double a;
    AA aa;

    doit(a);
    doit(aa);

    return 0;
}
查看更多
乱世女痞
3楼-- · 2020-02-01 08:57
double doit(AA &t) {
  return t.Plus(t);;
}
template<class T> double doit(T &t) {
  return t + t;
}

Your code doesn't work because if the template is deduced as AA then t + t inside the body is ill-formed. On the other hand if T is deduces as double then t.Plus(t) becomes ill-formed.

To better understand what is happening: A template is instantiated for each template type is called with.

doIt(a) instantiates doIt with T = double:

double doIt<double>(double &t) {
  if (false)
     return t.Plus(t);  // <-- syntax error
  else
    return t + t;
}

doIt(aa) instantiates doIt with T = AA:

double doIt<AA>(AA &t) {
  if (true)
    return t.Plus(t);
  else 
    return t + t;   // <-- syntax error
}

You should avoid specializing function templates because functions overload. You can read this excellent Herb Sutter article: Why Not Specialize Function Templates?

查看更多
Ridiculous、
4楼-- · 2020-02-01 09:00

Since C++17 there is static if which is called if-constexpr. The following compiles fine since clang-3.9.1 and gcc-7.1.0, latest MSVC compiler 19.11.25506 handles well too with an option /std:c++17.

template<class T> double doit(T &t) {
    if constexpr (std::is_same_v<T, AA>)
        return t.Plus(t);
    else
        return t + t;
}
查看更多
唯我独甜
5楼-- · 2020-02-01 09:04

Overloading?

template<class T>
double doit(T &t) {
    return t + t;
}

double doit(AA &t) {
    return t.Plus(t);
}

Explicit specialization is also possible, though superfluous:

template<class T>
double doit(T &t) {
    return t + t;
}

template<>
double doit<>(AA &t) {
    return t.Plus(t);
}
查看更多
够拽才男人
6楼-- · 2020-02-01 09:05

What you want is a static if. C++ doesn't have it. There are many ways to work around it, none as good as native support. In addition to the methods specified in the other two answers, you could try tag dispatch.

template<class T> double doitImpl(T &t, std::true_type) {
    return t.Plus(t);
}

template<class T> double doitImpl(T &t, std::false_type) {
    return t+t;
}

template<class T> double doit(T &t) {
    return doitImpl(t, std::is_same<T, AA>);
}
查看更多
登录 后发表回答