【问题标题】:C++ parameter pack with single type enforced in arguments在参数中强制执行单一类型的 C++ 参数包
【发布时间】:2018-05-08 07:51:05
【问题描述】:

我希望能够做到以下几点:

#include <array>
struct blah { };

template<typename... Args>
constexpr auto foo(Args&&... args)
{
    return std::array<blah, sizeof...(Args)>{{ args... }};
}

auto res = foo({}, {});

以下答案并不令人满意:他们只想检查参数包是否属于单一类型,但我想在参数中将值正确转换为它(否则它不起作用)。

C++ parameter pack, constrained to have instances of a single type?

Parameter with non-deduced type after parameter pack

Specifying one type for all arguments passed to variadic function or variadic template function w/out using array, vector, structs, etc?

我也不能使用 initializer_list,因为我无法计算要传递给 array 类型的参数数量。 而且我特别不想输入foo(blah{}, blah{});

我的可能性是什么?

【问题讨论】:

  • (附带问题:在某个时间点,stackoverflow 中没有直接集成 ideone 吗?我似乎记得能够直接在答案中测试运行 c++ 代码,但也许我只是梦想...)
  • 从来没有听说过这个
  • 我不知道你想要什么是可能的。编译器无法推断出 {} 是什么,我不确定是否有办法这样做。但是,像往常一样,一些 C++ 大师会通过提交 10 个基于元函数的答案来证明我错了。

标签: c++ c++11 c++14 variadic-templates c++17


【解决方案1】:

为懒惰者(C++17)稍微扩展approach of Jarod42

#include <utility>
#include <array>

struct blah {};

template <class T, std::size_t I>
using typer = T;

template <class T, std::size_t N, class = std::make_index_sequence<N>>
struct bar_impl;

template <class T, std::size_t N, std::size_t... Is>
struct bar_impl<T, N, std::index_sequence<Is...>> {
    static auto foo(typer<T, Is>... ts) {
        return std::array<T, N>{{ts...}};
    }
};

template <class T = blah, std::size_t N = 10, class = std::make_index_sequence<N>>
struct bar;

template <class T, std::size_t N, std::size_t... Is>
struct bar<T, N, std::index_sequence<Is...>>: bar_impl<T, Is>... {
    using bar_impl<T, Is>::foo...;
};

int main() {
    bar<>::foo({}, {});
}

[live demo]

编辑:

一些 C++14 解决方案(如 max66 所述)比我预期的还要简单:

#include <utility>
#include <array>

struct blah {};

template <class T, std::size_t I>
using typer = T;

template <class T = blah, std::size_t N = 10, class = std::make_index_sequence<N>>
struct bar;

template <class T, std::size_t N, std::size_t... Is>
struct bar<T, N, std::index_sequence<Is...>>: bar<T, N - 1> {
    using bar<T, N - 1>::foo;
    static auto foo(typer<T, Is>... ts) {
        return std::array<T, N>{{ts...}};
    }
};

template <class T>
struct bar<T, 0, std::index_sequence<>> {
    static auto foo() {
        return std::array<T, 0>{{}};
    }
};

int main() {
    bar<>::foo({}, {});
}

[live demo]

再修改:

这个(如 Jarod42 所建议的)提供的调用语法与 OP 的问题完全相同:

#include <utility>
#include <array>

struct blah {};

template <class T, std::size_t I>
using typer = T;

template <class T = blah, std::size_t N = 10, class = std::make_index_sequence<N>>
struct bar;

template <class T, std::size_t N, std::size_t... Is>
struct bar<T, N, std::index_sequence<Is...>>: bar<T, N - 1> {
    using bar<T, N - 1>::operator();
    auto operator()(typer<T, Is>... ts) {
        return std::array<T, N>{{ts...}};
    }
};

template <class T>
struct bar<T, 0, std::index_sequence<>> {
    auto operator()() {
        return std::array<T, 0>{{}};
    }
};

bar<> foo;

int main() {
    foo({}, {});
}

[live demo]

【讨论】:

  • 如果 barbar&lt;T, N-1U&gt; 递归继承并且 foo()bar 中定义,您可以避免 bar_impl 并使其也适用于 C++14(我想也是 C ++11 与 std::index_sequence) 的代理项
  • 太棒了!老实说,我根本不认为这是可能的。
【解决方案2】:

好吧,如果你能负担得起将语法更改为 little 一点,这是我设法找到的最好的:

#include <array>

// to_array implementation taken from 
// http://en.cppreference.com/w/cpp/experimental/to_array
namespace detail {
template <class T, std::size_t N, std::size_t... I>
constexpr std::array<std::remove_cv_t<T>, N>
    to_array_impl(T (&a)[N], std::index_sequence<I...>)
{
    return { {a[I]...} };
}
}

template <class T, std::size_t N>
constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N])
{
    return detail::to_array_impl(a, std::make_index_sequence<N>{});
}
// End of to_array implementation

struct blah { };

template<std::size_t N>
constexpr auto foo(const blah(&arr)[N])
{
    return to_array(arr);
}

int main()
{
    auto res = foo({{}, {}});
    return 0;
}

如您所见,foo({}, {}) 变成了foo({{}, {}})。 这是一个工作示例:https://ideone.com/slbKi3

您想要的方式 (foo({}, {})) 的问题是编译器无法知道应该将 {} 转换为什么。

我想办法让它知道,但它根本不听。

【讨论】:

  • 不错。但我想可以简化:如果你立即从foo() 调用to_array_impl()(你知道foo() 中的N,所以你可以从foo() 调用std::make_index_sequence&lt;N&gt;{})你可以避免to_array() 步骤。
【解决方案3】:

如果您接受 Telokis 建议的添加括号级别调用foo()

auto res = foo( { {}, {} } );

你可以使用 Telokis 提出的 C 风格的数组技巧和一个简单的循环来初始化返回值

template <std::size_t N>
constexpr std::array<blah, N> foo (const blah(&arr)[N])
 {
   std::array<blah, N> ret;

   for ( auto i = 0U ; i < N ; ++i )
      ret[i] = arr[i];

   return ret;
 }

不幸的是,std::arrayoperator[]constexpr 仅从 C++17 开始,因此前面的 foo 实际上是 constexpr 仅从 C++17 开始。

所以你可以打电话

auto res = foo( { {}, {} } );

也在 C++11 和 C++14 中,但是

constexpr auto res = foo( { {}, {} } );

仅从 C++17 开始。

【讨论】:

  • 你也可以使用实验性的 std::to_array 来代替手动循环,它有一个很好的包扩展实现:en.cppreference.com/w/cpp/experimental/to_array
  • @Jean-MichaëlCelerier - 当然,但这是 Telokis 的回答者;我的意图是提出一些不同的建议。
  • 你可以return std::array&lt;blah, sizeof...(Indx)&gt;{blah[Indx]...}; 使其兼容 C++11。
【解决方案4】:

保持语法的一种(有限)方法是使用多个重载:

constexpr auto foo(const blah& a1)
{
    return std::array<blah, 1>{{ a1 }};
}

constexpr auto foo(const blah& a1, const blah& a2)
{
    return std::array<blah, 2>{{ a1, a2 }};
}

// ...

// Up to N
constexpr auto foo(const blah& a1, const blah& a2, .., const blah& aN)
{
    return std::array<blah, N>{{ a1, a2, .., aN }};
}

W.F.在他的answer 中展示了一种生成它的方法,这要归功于类范围内的可变参数。

【讨论】:

  • 这不就是发明可变参数模板的原因吗?
【解决方案5】:

在 C++17 中,您可以像这样使用 static_assertstd::conjunction 的组合:

#include <array>
#include <type_traits>

struct blah {};

template <typename Arg, typename... Args>
constexpr auto foo_helper(Arg&& first, Args&&... rest) {
    
    static_assert(std::conjunction_v<std::is_same<Arg, Args>...>);
    
    return std::array<blah, 1 + sizeof...(Args)>{first, rest...};
}

template <typename... Args>
constexpr auto foo(Args&&... args) {
    return foo_helper(std::forward<Args>(args)...);
}

auto res = foo(blah{}, blah{})

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-05-12
    • 1970-01-01
    • 2018-09-26
    • 2016-08-13
    • 1970-01-01
    • 2021-04-02
    • 1970-01-01
    • 2011-04-24
    相关资源
    最近更新 更多