如果使用引用实例化,为什么使用非类型模板参数实例化我的模板会导致编译错误?

问题描述 投票:0回答:1

我有

value_list
结构模板:

template <decltype(auto)... values>
struct value_list
{};

我想实现一种能够通过索引访问模板参数的方法。 所以我最终得到了这个解决方案:

#include <type_traits>

namespace details
{
// Second parameter T is just to get rid of errors like this:
// Ambiguous partial specializations of 'at_impl<0, value_list<1>>'
template <std::size_t I, typename T, typename ValueList>
struct at_impl;

template <decltype(auto) Head, decltype(auto)... Rest>
struct at_impl<0, void, value_list<Head, Rest...>>
{
    static constexpr decltype(auto) value = Head;
};

template <std::size_t I, decltype(auto) Head, decltype(auto)... Rest>
struct at_impl<I, int, value_list<Head, Rest...>>
{
    static constexpr decltype(auto) value = at_impl<I-1, std::conditional_t<I-1 == 0, void, int>, value_list<Rest...>>::value;
};
}  // namespace details

template <std::size_t I, typename ValueList>
struct at
{
    static constexpr decltype(auto) value = details::at_impl<I, std::conditional_t<I == 0, void, int>, ValueList>::value;
};

template <std::size_t I, typename ValueList>
inline constexpr decltype(auto) at_v = at<I, ValueList>::value;

可以这样使用:

#include <iostream>

#define LOG(x) \
std::cout << #x << ": " << x << '\n'

int main()
{
    static constexpr int i = 5;
    const std::size_t index = 0;
    LOG((at_v<index, value_list<i>>));
    
    return 0;
}

输出:

(at_v<index, value_list<i>>): 5

但问题是,如果我们以引用作为参数实例化

value_list
模板,它就无法编译:

int main()
{
    static constexpr int i = 5;
    const std::size_t index = 0;

    constexpr const int & ri = i;
    LOG((at_v<index, value_list<ri>>));
    
    return 0;
}

错误:

implicit instantiation of undefined template 'details::at_impl<0, void, value_list<i>>'

如果

at_impl
包含引用,为什么
value_list
无法编译以及如何解决此问题?

如果您想使用我的代码,这里是 wanbox 的链接

c++ templates c++17
1个回答
0
投票

好吧,看来至少我弄清楚了如何修复我的代码,使其在所有三个主要编译器(msvc、gcc 和 clang)下同样工作。但在这种情况下,模板

at
应该位于
value_list
内部:

#include <tuple>
#include <type_traits>

template <decltype(auto)... Values>
struct value_list
{
    template <std::size_t I>
    using at_t = std::tuple_element_t<I, std::tuple<decltype(Values)...>>;

    template <std::size_t I>
    static constexpr at_t<I> at_v = std::get<I>(std::tuple<decltype(Values)...>(Values...));
};

int main()
{
    static constexpr int i = 5;
    constexpr const int & ri = i;
    
    using list = value_list<'t', ri, i, 10>;
    
    static_assert(std::is_same_v<decltype(list::at_v<0>), const char>);
    static_assert(std::is_same_v<decltype(list::at_v<1>), const int &>);
    static_assert(std::is_same_v<decltype(list::at_v<2>), const int>);
    static_assert(std::is_same_v<decltype(list::at_v<3>), const int>);
    static_assert(list::at_v<0> == 't');
    static_assert(list::at_v<1> == 5);
    static_assert(list::at_v<2> == 5);
    static_assert(list::at_v<3> == 10);
    static_assert(std::is_same_v<list::at_t<0>, char>);
    static_assert(std::is_same_v<list::at_t<1>, const int &>);
    static_assert(std::is_same_v<list::at_t<2>, int>);
    static_assert(std::is_same_v<list::at_t<3>, int>);
    
    return 0;
}

我已检查此代码是否已成功编译:

  • x64 msvc v19.38
  • AppleClang 14.0.0.14000029
  • 海湾合作委员会12.3.0

关于我的问题中提供的“为什么”代码无法编译,我没有比“与 gcc 和 msvc 不同,由于某种原因 clang++ 无法编译此代码”更好的答案。

作为旁注: 在阅读了@TedLyngmo在评论部分提供的Sam的答案之一后,我仍然不明白为什么

static_assert(std::is_same_v<list::at_t<2>, int>);
被编译了?因为索引为 2 的参数是
i
,它是未加括号的 id 表达式(我错了吗?),那么根据 C++17 标准
decltype(i)
应该是由
i
命名的实体的类型。我们可以清楚地看到,类型是
const int
(不是
int
),因为
constexpr
意味着
const
。但同时我怀疑所有三个主要编译器都可能是错误的。相反,我没有考虑某些事情,这是另一个问题的主题。

© www.soinside.com 2019 - 2024. All rights reserved.