Using std::enable_if with anonymous type parameters

两盒软妹~` 提交于 2019-12-01 04:57:36

However, my intention is to get some more details why it does not work if I use it with anonymous type parameters.

Default values do not participate in overload resolution, thus you are actually redefining the same function.

Let's simplify your example:

template<typename = int>
void f() {}

template<typename = void>
void f() {}

int main() {
    f<>();
}

The code above does not compile, for it couldn't know what version of f you want to invoke.

In your case, if I invoke foo as foo<void, void>, I've almost the same problem.
The compiler cannot guess what's my intention and the fact that the second parameter has a default value doesn't mean that you can't pass in a different type.

Because of that, the code is ill-formed and the compiler correctly gives you an error.


As a side note, you can still have it working without using the std::enable_if_t in the return type.
As an example:

#include <type_traits>
#include <iostream>

template <typename T, std::enable_if_t<!std::is_integral<T>::value>* = nullptr>
T foo() { std::cout << "non-integral" << std::endl; return T(); }

template <typename T, std::enable_if_t<std::is_integral<T>::value>* = nullptr>
T foo() { std::cout << "integral" << std::endl; return T(); }

int main() {
    foo<float>();
    foo<int>();
}

While I tried to figure out what was the (wrong) assumption of the OP and explain why it can be the case, @T.C. correctly pointed the attention out to the actual reason in the comments to this answer.
It's worth to quote his comment to add more details to the answer:

It's not overload resolution; it's declaration matching. There are no two overloads in the first place for any ambiguity to arise. It's two redefinition errors: the function template, and the default template argument.

You can put enable_if into the return type:

template <typename T>
std::enable_if_t<!std::is_integral<T>::value,T>
foo() { std::cout << "non-integral" << std::endl; return T(); }

template <typename T>
std::enable_if_t<std::is_integral<T>::value, T>
foo() { std::cout << "integral" << std::endl; return T(); }

By the way, enable_if_t is available from C++14, so you might want to say typename std::enable_if<std::is_integral<T>::value, T>::type instead. Quite a mouthful.

But a little more idiomatic (and readable) would be to dispatch basing on the type:

template <typename T>
T foo_impl(std::false_type) { std::cout << "non-integral" << std::endl; return T(); }

template <typename T>
T foo_impl(std::true_type) { std::cout << "integral" << std::endl; return T(); }

template <typename T>
T foo(){
    return foo_impl<T>(typename std::is_integral<T>::type{});
}

There are a couple of ways you can SFINAE away functions. You should usually refrain from adding an extra function/template parameter and just mingle with the return type.

template <typename T>
auto foo() -> std::enable_if_t<!std::is_integral<T>::value, T>
{ std::cout << "non-integral" << std::endl; return T(); }

template <typename T>
auto foo() -> std::enable_if_t<std::is_integral<T>::value, T>
{ std::cout << "integral" << std::endl; return T(); }

Your error is that you're using enable_if_t on the right of the equal sign.

You have to use it on the left

#include <iostream>
#include <type_traits>

template <typename T, std::enable_if_t<!std::is_integral<T>::value, int> = 0>
T foo() { std::cout << "non-integral" << std::endl; return T(); }

template <typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>
T foo() { std::cout << "integral" << std::endl; return T(); }

int main() {
  foo<float>();
  foo<int>();
}

But this work with C++14.

In C++11 (your question is tagged C++11) you don't have enable_if_t.

The code become

#include <iostream>
#include <type_traits>

template <typename T,
          typename std::enable_if<!std::is_integral<T>::value, int>::type = 0>
T foo() { std::cout << "non-integral" << std::endl; return T(); }

template <typename T,
          typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
T foo() { std::cout << "integral" << std::endl; return T(); }

int main() {
  foo<float>();
  foo<int>();
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!