0

I have a code that should implement SFINAE to resolve between two different methods, which however have a different template signature: one has an additional non-type template parameter. As suggested here, for example, one should better implement the SFINAE in the template parameters.

The compiler however issues an "ambiguous call" error.

This (simplified) example illustrates the issue

#include <iostream>
#include <type_traits>

template <bool X>
class A {
   template <unsigned int I>
   void print_true_impl();
   
   void print_false_impl();

public:
   template <unsigned int I, bool activate = X, class = std::enable_if_t<activate>>
   void print() { print_true_impl<I>(); }
   
   template <bool activate = !X, class = std::enable_if_t<activate>>
   void print() { print_false_impl(); }
};

template <bool X>
template <unsigned int I>
void A<X>::print_true_impl()
{
    std::cout << "true " << I << std::endl;
}

template <bool X>
void A<X>::print_false_impl()
{
    std::cout << "false" << std::endl;
}

int main()
{
  A<true> atrue;
  A<false> afalse;

  afalse.print();
  atrue.print<1>();
  return 0;
}

Check it live on Coliru.

The compile error appears on

atrue.print<1>();

The reason for ambiguity lies in the fact that the template parameter <1> is implicitly converted to bool, hence the compiler can pick up the overload

template <bool activate = !X, class = std::enable_if_t<activate>>
   void print() { print_false_impl(); }

setting activate = true, and ignoring the default activate = !X (=false when called from atrue).

Ideally one would like to avoid the implicit conversions in the template parameters, such that bool activate is always set to the default value.

So, one way to solve the ambiguity would be to prepend the templates with a class to which an unsigned int cannot converted to. For example

   template <unsigned int I, class = A, bool activate = X, class = std::enable_if_t<activate>>
   void print() { print_true_impl<I>(); }
   
   template <class = A, bool activate = !X, class = std::enable_if_t<activate>>
   void print() { print_false_impl(); }

This works, however it seems inelegant to artificially add a useless template parameter.

Another alternative would be to pack the choice of overload inside a single method using if-constexpr, like:

#include <iostream>
#include <type_traits>


template <auto...>
inline constexpr bool dependent_false_v = false;

template <bool X>
class A {
   template <unsigned int I>
   void print_true_impl();
   
   void print_false_impl();

public:

    template <unsigned int... I>
    void print() {
        if constexpr (sizeof...(I) == 0) {
            print_false_impl();
        }
        else if constexpr (sizeof...(I) == 1) {
            print_true_impl<I...>();
        }
        else {
            static_assert(dependent_false_v<I...>, "Cannot call print()!");
        }
    }
};

Is there a better solution, within c++17 (no concepts), and using SFINAE and not if-constexpr?

francesco
  • 7,189
  • 7
  • 22
  • 49
  • With given example, full specialization :) `A`/`A`. – Jarod42 May 12 '23 at 12:45
  • Do you really need SFINAE friendly here? `static_assert` might be sufficient. – Jarod42 May 12 '23 at 12:47
  • 2
    Interface seems strange BTW, IMO. – Jarod42 May 12 '23 at 12:48
  • @Jarod42 This is of course a simplified example, in the real code I cannot full specialized because I have >1 template parameters in A. ```static_assert```: uhm, you're right I think. Removed ```activate``` and ```std::enable_if```, the two overload would be no longer ambiguous and a static_assert would prevent, say, ```A::print()```. Is that what you had in mind? – francesco May 12 '23 at 12:54
  • It is what I have in mind. – Jarod42 May 12 '23 at 13:05
  • @Jarod42 OK, this is indeed a solution that suits my case. Thanks a lot! – francesco May 12 '23 at 13:09
  • you could also declare `enum class eActivated: bool {kFalse = false, kTrue = true};` and use it as template parameter instead of `bool activate = X/!X`. Here is example: https://coliru.stacked-crooked.com/a/3fe45ae7c244d194 – dummy Aug 01 '23 at 10:04

0 Answers0