C++ 参数包扩展与概念

Евг*_*цын 6 c++ templates language-lawyer template-meta-programming c++20

如果取消注释行 #1 和注释行 #2,为什么会出现编译错误?演示: https: //godbolt.org/z/KW6dhsrKd

#include <utility>

template <typename, std::size_t> concept prefix = true;

template<std::size_t>
struct dummy { template<typename T> constexpr dummy(T){}; };

template <auto N>
consteval auto nth_element(auto... args) 
{
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
    //return [](prefix<Is> auto..., auto arg, auto...) { //compile error // #1
    return [](dummy<Is> ..., auto arg, auto...) { // OK                  // #2
    return arg;
    }(args...);
}(std::make_index_sequence<N>());
}

int main()
{
    static_assert(nth_element<0>(1, 2, 3) == 1);
    static_assert(nth_element<1>(1, 2, 3) == 2);
    static_assert(nth_element<2>(1, 2, 3) == 3);
    
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Col*_*mbo 3

来自[dcl.fct]/22

缩写函数模板相当于函数模板 ([temp.fct]),其模板参数列表按出现顺序为函数声明的每个泛型参数类型占位符包含一个发明的类型模板参数。[..]如果相应的参数声明声明了函数参数包,则所发明的类型模板参数是模板参数包。

请注意,这prefix<Is> auto...既是包又是包扩展。
所以转换后的模板看起来像

[] <prefix<Is>... Args> (Args..., auto arg, auto...) {
    return arg;
}(args...)
Run Code Online (Sandbox Code Playgroud)

这相当于

[] <typename... Args> requires(prefix<Args, Is> && ...) (Args..., auto arg, auto...) {
    return arg;
}(args...)
Run Code Online (Sandbox Code Playgroud)

(这仍然会生成相同的错误消息。)
我们可以看到 1) Args 处于非推导上下文中并且将始终保持为空,2)Is 并且Args必须在长度上达成一致才能满足约束。

在我看来,评论中给出的解决方案并不理想,因为创建元组可能相对昂贵(尤其是在编译时)。我认为你的解决方案+正确的转发是目前最好的(直到我们得到正确的包下标)。即像这样:

template<std::size_t>
struct dummy { constexpr dummy(auto&&){}; };

template <std::size_t N, typename... Args>
constexpr decltype(auto) nth_element(Args&&... args) {
    return [&]<std::size_t... Is>(std::index_sequence<Is...>) -> decltype(auto) {
        return [](dummy<Is> ..., auto&& arg, auto&&...) -> decltype(auto) { // OK                  // #2
            return std::forward<decltype(arg)>(arg);
        }(std::forward<Args>(args)...);
    }(std::make_index_sequence<N>());
}
Run Code Online (Sandbox Code Playgroud)