jeudi 4 avril 2019

SFINAE: Ordering of function overloads matter?

I encountered a situation where the ordering of function overloads matters when using SFINAE. The cause must be the SFINAE part with the std::enable_if ((I additionally tested the code without the std::enable_if, just with templates and then the code runs, regardless the ordering of function overloads.))

I made a minimal working example out of it.

This code block works

#include <type_traits>

template <bool C, typename R = void>
using EnableIf = typename std::enable_if<C, R>::type;

template <typename T, typename R = void>
using IfIsArithmetic = EnableIf<std::is_arithmetic<T>::value, R>;

template <typename S>
IfIsArithmetic<S, void> Add(S const &scalar1, S const &scalar2, S &ret)
{
 ret = scalar1 + scalar2;
}

template <typename S>
IfIsArithmetic<S, S> Add(S const &scalar1, S const &scalar2)
{
 S ret;
 Add(scalar1, scalar2, ret);
 return ret;
}

using T = float;

int main(){
  T a = 3.1;
  T b = 3.5;
  T c{Add(a, b)};
}

whereas the following doesn't compile (the implementations of the Add() functions are swapped)

#include <type_traits>

template <bool C, typename R = void>
using EnableIf = typename std::enable_if<C, R>::type;

template <typename T, typename R = void>
using IfIsArithmetic = EnableIf<std::is_arithmetic<T>::value, R>;

template <typename S>
IfIsArithmetic<S, S> Add(S const &scalar1, S const &scalar2)
{
 S ret;
 Add(scalar1, scalar2, ret);
 return ret;
}

template <typename S>
IfIsArithmetic<S, void> Add(S const &scalar1, S const &scalar2, S &ret)
{
 ret = scalar1 + scalar2;
}

using T = float;

int main(){
  T a = 3.1;
  T b = 3.5;
  T c{Add(a, b)};
}

The compiler gives the following error

test.cpp: In instantiation of ‘IfIsArithmetic<S, S> Add(const S&, const S&) [with S = float; IfIsArithmetic<S, S> = float]’:
test.cpp:28:15:   required from here
test.cpp:13:5: error: no matching function for call to ‘Add(const float&, const float&, float&)’
  Add(scalar1, scalar2, ret);
  ~~~^~~~~~~~~~~~~~~~~~~~~~~
test.cpp:10:22: note: candidate: template<class S> IfIsArithmetic<S, S> Add(const S&, const S&)
 IfIsArithmetic<S, S> Add(S const &scalar1, S const &scalar2)
                      ^~~
test.cpp:10:22: note:   template argument deduction/substitution failed:
test.cpp:13:5: note:   candidate expects 2 arguments, 3 provided
  Add(scalar1, scalar2, ret);
  ~~~^~~~~~~~~~~~~~~~~~~~~~~

It seems, that the compiler tries to use the first function overload, even though the signature doesn't align.

Maybe some of you can give me hint, to which C++ function lookup rule this issue is related!

Aucun commentaire:

Enregistrer un commentaire