std::enable_if 用于 std::is_integral 及其否定形式都显示为模糊的候选重载。

huangapple go评论70阅读模式
英文:

std::enable_if for std::is_integral and its negation both show as ambiguous candidate overloads

问题

Here's the translated code:

// 用于在缺少重载时提供易读的错误
template<typename T>
struct fail_if_invoked : std::false_type
{ };

// 如果值不是整数类型且没有显式重载,则应选择此重载
template<typename TVal, typename std::enable_if<!std::is_integral<TVal>::value>::type* = nullptr>
void accept_value(TVal && vadd)
{
    static_assert(fail_if_invoked<TVal>::value, "Missing implementation for this type");
}

// 此重载应处理所有整数类型
template<typename TInteger, typename std::enable_if<std::is_integral<TInteger>::value>::type* = nullptr>
void accept_value(TInteger num)
{
    ...
}

void accept_value(const char*) 
{
    ...
}

Regarding your questions:

  1. The void accept_value(TVal && vadd) function is still in the list because SFINAE (Substitution Failure Is Not An Error) doesn't completely eliminate it. It's just that the compiler marks it as a candidate with a lower priority due to the std::enable_if. In your specific case with unsigned int, both the integral overload and the generic one are viable candidates, leading to an ambiguous call.

  2. It is possible to have a generic implementation for all integral types while also having bool and double overloads, but you need to refine your SFINAE conditions to ensure that there's no overlap between the overloads. In this case, the overlap between the generic overload and the integral overload is causing the ambiguity. You might need to use more specific type traits to differentiate them.

英文:

I have some API that serializes values. I have created a wrapper that helps by having a simple one method that should accept all supported types.

Most types require explicit implementation, but all integer types are accepted by single method on that API. I wrote this code:

// Used to allow for human readable error on missing overload
template&lt;typename T&gt;
struct fail_if_invoked : std::false_type
{ };

// This overload should get selected if value is not integer type and explicit overload does not exist
template&lt;typename TVal, typename std::enable_if&lt;!std::is_integral&lt;TVal&gt;::value&gt;::type* = nullptr&gt;
void accept_value(TVal &amp;&amp; vadd)
{
    static_assert(fail_if_invoked&lt;TVal&gt;::value, &quot;Missing implementation for this type&quot;);
}

// This overload should handle all integer types
template&lt;typename TInteger, typename std::enable_if&lt;std::is_integral&lt;TInteger&gt;::value&gt;::type* = nullptr&gt;
void accept_value(TInteger num)
{
    ...
}

void accept_value(const char*) 
{
    ...
}

However, when unsigned int is the argument to accept_value, I get an error about ambiguous call, for these overloads:

my_helper.h:37:10: note: candidate function [with TVal = unsigned short &amp;, $1 = nullptr]
    void accept_value(TVal &amp;&amp; vadd)
         ^
my_helper.h:44:10: note: candidate function [with TInteger = unsigned short, $1 = nullptr]
    void accept_value(TInteger num)
         ^
my_helper.h:59:10: note: candidate function
    void accept_value(double num)
         ^
my_helper.h:79:10: note: candidate function
    void accept_value(bool num)

I sort of understand the bool and double, although it wasn't an issue when I had explicit overloads for everything. But why is the void add_value(TVal &amp;&amp; vadd) still in the list when !std::is_integral&lt;TVal&gt; should disable it?

And is it even possible to have generic implementation for all integral types while also having bool and double overloads?

答案1

得分: 3

你需要将模板参数用 std::decay_t 包裹起来,以获得没有引用或常量修饰的原始类型,因为 std::integral&lt;int&amp;&gt; 会被判断为假。

更新 - 额外说明

签名 accept_value(TVal &amp;&amp; vadd) 使用右值引用(通常称为通用引用)接受参数。如果你传递类型为 int 的变量,TVal 将被推导为 int&amp;。如果你传递字面整数如 1TVal 将被推导为 int。如果没有使用 std::decay_t,在评估 std::is_integral 时会导致不同的结果。重载决议会相应地行为。

更新结束

示例代码

#include <iostream>

// 用于在缺少重载时提供人类可读的错误
template<typename T>
struct fail_if_invoked : std::false_type
{ };

// 如果值不是整数类型且不存在显式重载,将选择这个重载
template<typename TVal,
         typename std::enable_if<!std::is_integral<std::decay_t<TVal>>::value>::type* = nullptr>
void accept_value(TVal && vadd)
{
    static_assert(fail_if_invoked<TVal>::value, "Missing implementation for this type");
}

// 这个重载应该处理所有整数类型
template<typename TInteger,
         typename std::enable_if<std::is_integral<std::decay_t<TInteger>>::value>::type* = nullptr>
void accept_value(TInteger num)
{
}

int main(int argc, const char *argv[]) {
    int a{};
    accept_value(a);
    accept_value(1);
    accept_value("char");
    return 0;
}

输出

/work/so/scratch/src/p1.cpp:16:5: 错误: 静态断言失败,要求 'fail_if_invoked<const char (&)[5]>::value': 缺少此类型的实现
    static_assert(fail_if_invoked<TVal>::value, "Missing implementation for this type");
    ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/work/so/scratch/src/p1.cpp:30:5: 注意: 在此处请求了函数模板 specialization 'accept_value<const char (&)[5], nullptr>'
    accept_value("char");
    ^
1 error generated.

更新

顺便说一下,在 c++20 中会更容易一些

template<class T> requires (not std::is_integral_v<std::decay_t<T>>)
void accept_value(T&& vadd)
{
    static_assert(std::is_integral_v<std::decay_t<T>>, "Missing implementation for this type");
}

template<class T> requires (std::is_integral_v<std::decay_t<T>>)
void accept_value(T&& num)
{
}
英文:

You need to wrap the template parameter in std::decay_t to get the raw type without any reference or const, because std::integral&lt;int&amp;&gt; will evaluate to false.

Update - additional explanation

The signature accept_value(TVal &amp;&amp; vadd) accepts the parameter using an rvalue-reference (often called a universal reference). If you pass a variable of type int, TVal will deduce to int&amp;. If you pass a literal integer like 1, TVal will deduce to int. Without the std::decay_t, this will cause different results when std::is_integral is evaluated. Overload resolution will behave accordingly.

End Update

Sample Code

#include &lt;iostream&gt;

// Used to allow for human readable error on missing overload
template&lt;typename T&gt;
struct fail_if_invoked : std::false_type
{ };

// This overload should get selected if value is not integer type and explicit overload does not ex\
ist
template&lt;typename TVal,
         typename std::enable_if&lt;!std::is_integral&lt;std::decay_t&lt;TVal&gt;&gt;::value&gt;::type* = nullptr&gt;
void accept_value(TVal &amp;&amp; vadd)
{
    static_assert(fail_if_invoked&lt;TVal&gt;::value, &quot;Missing implementation for this type&quot;);
}

// This overload should handle all integer types
template&lt;typename TInteger,
         typename std::enable_if&lt;std::is_integral&lt;std::decay_t&lt;TInteger&gt;&gt;::value&gt;::type* = nullptr&gt;
void accept_value(TInteger num)
{
}

int main(int argc, const char *argv[]) {
    int a{};
    accept_value(a);
    accept_value(1);
    accept_value(&quot;char&quot;);
    return 0;
}

Output

/work/so/scratch/src/p1.cpp:16:5: error: static assertion
      failed due to requirement &#39;fail_if_invoked&lt;const char (&amp;)[5]&gt;::value&#39;: Missing
      implementation for this type
    static_assert(fail_if_invoked&lt;TVal&gt;::value, &quot;Missing implementation for this type&quot;);
    ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/work/so/scratch/src/p1.cpp:30:5: note: in instantiation of
      function template specialization &#39;accept_value&lt;const char (&amp;)[5], nullptr&gt;&#39; requested here
    accept_value(&quot;char&quot;);
    ^
1 error generated.

Update

As an aside, this gets much easier with c++20

template&lt;class T&gt; requires (not std::is_integral_v&lt;std::decay_t&lt;T&gt;&gt;)
void accept_value(T&amp;&amp; vadd)
{
    static_assert(std::is_integral_v&lt;std::decay_t&lt;T&gt;&gt;, &quot;Missing implementation for this type&quot;);
}

template&lt;class T&gt; requires (std::is_integral_v&lt;std::decay_t&lt;T&gt;&gt;)
void accept_value(T&amp;&amp; num)
{
}

huangapple
  • 本文由 发表于 2023年5月17日 19:07:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/76271413.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定