c++ - C++20 可变参数函数中的多个概念约束参数包不接受第一个参数包中的参数

标签 c++ overloading variadic-templates c++20 c++-concepts

以下代码使用多个参数包来定义接受任何数字类型或指向数字类型的指针的可变参数模板累加器函数:

// Compile with --std=c++20

#include <type_traits>

template <typename T>
concept number = std::is_arithmetic_v<T>
             && !std::is_pointer_v<T>;

template <typename T>
concept pointer = std::is_arithmetic_v<std::remove_pointer_t<T>>
               && std::is_pointer_v<T>;


double foo ()
{return 0;}

double foo (pointer auto     p0)
{return *p0;}

double foo (pointer auto     p0,
            pointer auto ... ps)
{return *p0 + foo (ps ...);}

double foo (number  auto     n0,
            pointer auto ... ps)
{return n0 + foo (ps ...);}

double foo (number  auto     n0,
            number  auto ... ns,  /* <---- THIS LINE */
            pointer auto ... ps)
{return n0 + foo (ns ..., ps ...);}


int main()
{
    float f = 3.;
    unsigned u = 4;
    
    foo (); // Compiles
    foo (1); // Compiles
    foo (&f); // Compiles
    foo (1, &f); // Compiles
    foo (1, &f, &u); // Compiles
    foo (&f, &u); // Compiles
    
    foo (1, 2.); // Error!
    foo (1, 2., &f); // Error!
    foo (1, 2., &f, &u); // Error!
}

当有多个number 类型的参数时会触发错误。

看起来当有多个参数包时,编译器将所有参数打包在最后一个参数包中,而不是引用约束来定义哪个参数属于哪个参数包。

这是语言的限制吗?多个参数包是否意味着以其他方式使用?是否有任何解决方法可以使其正常工作?

在 clang 和 GCC 中测试

更新:已解决!

解决方案:使用单个参数包,不对参数包进行约束,对参数的类型逐一进行约束。

// Compile with --std=c++20

#include <type_traits>

template <typename T>
concept number = std::is_arithmetic_v<T>;

template <typename T>
concept pointer = std::is_arithmetic_v<std::remove_pointer_t<T>>
               && std::is_pointer_v<T>;


double foo ()
{return 0;}

double foo (pointer auto     p0)
{return *p0;}

double foo (pointer auto     p0,
            pointer auto ... ps)
{return *p0 + foo (ps ...);}

template <typename ... N_P>
double foo (number auto n0,
            N_P ... ps)
{return n0 + foo (ps ...);}


int main()
{
    float f = 3.;
    unsigned u = 4;
    
    foo (); // Compiles
    foo (1); // Compiles
    foo (&f); // Compiles
    foo (1, &f); // Compiles
    foo (1, &f, &u); // Compiles
    foo (&f, &u); // Compiles
    
    foo (1, 2.); // Good!
    foo (1, 2., &f); // Good!
    // foo (1, &f, 2.); // Does not compile (Good!)
    return foo (1, 2., &f, &u); // Good!
}

最佳答案

函数参数包的推导只发生在参数列表中的最后包。所有其他包都是 considered a non-deduced context :

The non-deduced contexts are:

...

A function parameter pack that does not occur at the end of the parameter-declaration-list.

概念对此没有影响。您不能使用概念作为使第一个包可推导的方法。

无论如何,拥有一个可以是算术类型或指向算术类型的指针、折叠表达式和 single function to distinguish which from which 的概念要容易得多。 :

#include <type_traits>

template <typename T>
concept number = std::is_arithmetic_v<T>; //Pointers aren't arithmetic types.

template <typename T>
concept ptr_to_num =
    std::is_pointer_v<T> &&
    number<std::remove_pointer_t<T>>;

template<typename T>
concept ptr_to_num_or_num =
    number<T> || ptr_to_num<T>;

template<ptr_to_num_or_num T>
double dereference(T p)
{
    if constexpr(ptr_to_num<T>)
        return *p;
    else
        return p;
}

template<ptr_to_num_or_num ...Args>
double foo(Args ...args)
{
    return (0.0 + ... + dereference(args));
}

int main()
{
    float f = 3.;
    unsigned u = 4;
    
    foo (); // Compiles
    foo (1); // Compiles
    foo (&f); // Compiles
    foo (1, &f); // Compiles
    foo (1, &f, &u); // Compiles
    foo (&f, &u); // Compiles
    
    foo (1, 2.); // Error!
    foo (1, 2., &f); // Error!
    foo (1, 2., &f, &u); // Error!
}

是的,您将能够在数字之前传递指针。但无论您尝试做什么,这不是一个更好的界面吗?

关于c++ - C++20 可变参数函数中的多个概念约束参数包不接受第一个参数包中的参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63101304/

相关文章:

c++ - 如何在模板函数中根据类型构造不同的对象?

c++ - 数组作为指针传递的不明确重载

c++ - 为什么 clang 将字符串文字作为指针而不是数组?

c++ - 来自模板参数的扩展参数包(值)

c++ - 寻找使用 AgentX 实现 SNMP 表的示例代码

c# - SetWinEventHook 窗口最大化事件

c++ - 使用openCV的卷积神经网络

c++ - 概念是由看似会产生无效表达式的类型满足的

c++ - 以下哪一种函数类型不能被重载?

C++ 将可变参数模板参数扩展为语句