考虑一个例子:
#include <utility>
template <class... Ts>
struct pack {
static constexpr std::size_t size = sizeof...(Ts);
};
template <class P, class = std::make_index_sequence<P::size>>
struct ipack;
template <class... Ts, std::size_t... Is>
struct ipack<pack<Ts...>, std::index_sequence<Is...>> {
static constexpr std::size_t size = sizeof...(Ts);
};
template <class IP, class = std::make_index_sequence<IP::size>>
struct vpack;
template <class... Ts, std::size_t... Is>
struct vpack<ipack<pack<Ts...>>, std::index_sequence<Is...>> {
static constexpr std::size_t size = sizeof...(Ts);
};
int main() {
vpack<ipack<pack<int, int, int>>> vp;
static_cast<void>(vp);
}
Run Code Online (Sandbox Code Playgroud)
clang 报告问题:
prog.cc:29:39: error: implicit instantiation …
Run Code Online (Sandbox Code Playgroud) 如果我正确理解了这个答案并引用了标准部分[dcl.type.auto.deduct-5],那么代码如下:
decltype(auto) a = e;
Run Code Online (Sandbox Code Playgroud)
总是相当于
decltype( e ) a = e;
Run Code Online (Sandbox Code Playgroud)
但是现在问题出现了,而不是e
我把lambda表达式放到decltype(auto)
:
decltype(auto) lambda = [](){};
Run Code Online (Sandbox Code Playgroud)
令我惊讶的是,这在gcc和clang中成功编译.我所经历的冲击的原因在于标准,其中特别指出lambda不应出现在未评估的操作数中[expr.prim.lambda#2](强调我的):
lambda-expression是一个prvalue,其结果对象称为闭包对象.lambda表达式不应出现在未评估的操作数,模板参数,别名声明,typedef声明或函数体和默认参数之外的函数或函数模板的声明中.
但正如我所提到的,这个例子相当于:
decltype([](){}) lambda = [](){};
Run Code Online (Sandbox Code Playgroud)
明确写出的上述代码显然是不正确的.当然,我们可以假设[](){}
里面的语句decltype
是一种引用,实际上并不是像结构化绑定那样的引用,但是标准中有一条特殊的规则,我错过了覆盖lambda的初始化decltype(auto)
?
在这个答案的背景下,问题就出现了.
考虑一个例子:
struct foo {
int value;
operator int&(){ return value; }
operator int(){ return value; }
};
int main () {
int &a(foo{}); // #1
//int &b{foo{}}; // #2 -- ambiguity
int &c = foo{}; // #3
//int &d = {foo{}}; // #4-- ambiguity
int &d { a }; // #5
int &e = { a }; // #6
(void)a;
(void)c;
(void)d;
(void)e;
}
Run Code Online (Sandbox Code Playgroud)
我不明白为什么#2和#4引起歧义,而#1和#3则不然.所以问题是 - 为什么直接列表初始化会导致隐式强制转换引用的歧义,如果声明类型和对类型的引用的转换运算符?
考虑一个简单的例子:
int foo() {
return 3;
}
template <int>
struct Bar {};
int a;
int main() {
int b;
//Bar<((void)foo(), 1)> bar1; //case 1. compilation error as expected
Bar<((void)a, 2)> bar2; //case 2. no error (long shot but `a' has a linkage so maybe expected)
Bar<((void)b, 3)> bar3; //case 3. no error ? (`b' does not have linkage)
(void)bar2;
(void)bar3;
}
Run Code Online (Sandbox Code Playgroud)
考虑可能的实施std::apply
:
namespace detail {
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_impl(F &&f, Tuple &&t, std::index_sequence<I...>)
{
return std::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...);
}
} // namespace detail
template <class F, class Tuple>
constexpr decltype(auto) apply(F &&f, Tuple &&t)
{
return detail::apply_impl(
std::forward<F>(f), std::forward<Tuple>(t),
std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{});
}
Run Code Online (Sandbox Code Playgroud)
为什么在调用f
带有参数元组的function()来传递(t
)时,我们不需要在实现std::forward
中对元组的每个元素std::get<I>(std::forward<Tuple>(t))...
执行?
又一个decltype(auto)
模板模板参数问题.这次我能够创建的最小代码重现错误如下所示:
template <template <decltype(auto)> class TT, decltype(auto) V>
void foo(TT<V>) {
};
template <decltype(auto)>
struct Bar{};
int x;
int main() {
foo(Bar<(x)>{});
}
Run Code Online (Sandbox Code Playgroud)
这在[clang]中导致:
prog.cc:11:5: error: no matching function for call to 'foo'
foo(Bar<(x)>{});
^~~
prog.cc:2:6: note: candidate template ignored: substitution failure [with TT = Bar]: non-type template argument is not a constant expression
void foo(TT<V>) {
^
1 error generated.
Run Code Online (Sandbox Code Playgroud)
[gcc]接受代码.
根据我的理解,代码格式正确,并且clang在解释方面存在错误,但在向lvvm提交错误之前需要确认.我对吗?
考虑一个例子:
#include <type_traits>
template <class... Ts>
decltype (auto) foo(Ts... ts) {
return (ts->x + ...);
}
struct X {
int x;
};
int main() {
X x1{1};
static_assert(std::is_reference_v<decltype(foo(&x1))>);
}
Run Code Online (Sandbox Code Playgroud)
decltype(auto)
从带括号的左值推导出的,应根据[cl.type.simple] /4.4推导出左值参考.例如:
decltype(auto) foo(X *x) { // type of result == int&
return (x->x);
}
Run Code Online (Sandbox Code Playgroud)
但是snipped会触发static_assert.即使我们将表达式组合成额外的括号,例如:
return ((ts->x + ...));
Run Code Online (Sandbox Code Playgroud)
它不会改变效果.
标准中是否有一点可以防止将单个元素的折叠表达式推导到左值参考中?
编辑
作为Johannes Schaub的一个重点- litb clang实际上确实将代码的双parens版本解释为带括号的左值并推导出左值引用.在这种情况下,我会将其解释为gcc错误.然而,具有单parens版本的版本仍然存在问题.让我感到困惑的是,至少在多个元素的情况下,必须将版本转换为带括号的代码 - 以实现运算符优先级.例如:
(x + ...)*4 -> (x1 + x2)*4
Run Code Online (Sandbox Code Playgroud)
不一致的原因是什么?
考虑简单的例子:
template <auto(*X)()>
struct Foo {
decltype(X()) x;
};
int bar();
int main() {
static_cast<void>(Foo<bar>{});
}
Run Code Online (Sandbox Code Playgroud)
无论[GCC]和[铛]似乎接受的代码.代码真的符合c ++ 17吗?如果是这样,还有其他一些规则会使下面的代码生成错误吗?
template <class T, auto(*X)(T)>
struct Foo {
decltype(X(0)) x;
};
int bar(int);
int main() {
static_cast<void>(Foo<int, bar>{});
}
Run Code Online (Sandbox Code Playgroud)
这个只让[gcc]不高兴.
错误信息:
prog.cc: In function 'int main()':
prog.cc:9:35: error: unable to deduce 'auto (*)(T)' from 'bar'
static_cast<void>(Foo<int, bar>{});
^
prog.cc:9:35: note: mismatched types 'T' and 'int'
Run Code Online (Sandbox Code Playgroud) 我真的很惊讶gcc和clang都接受这个代码:
#include <iostream>
#include <vector>
#include <type_traits>
template <class T, template <class, class = T> class TT, class Y>
T foo(TT<Y>) {
}
int main() {
static_assert(std::is_same<decltype(foo(std::vector<int>{})), std::allocator<int>>::value);
}
Run Code Online (Sandbox Code Playgroud)
gcc和clang是正确的,默认模板模板参数的值是推导上下文还是编译器扩展?
我得到的最小例子有点复杂:
struct A { };
template <int>
struct Parent { };
template <int N>
constexpr int operator*(A, Parent<N>*) { return N; }
template <class T>
using ptr = T*;
template <int>
struct Other { };
template <int N>
struct Kid: Parent<N> {
static Other<A{} * ptr<Kid>{}> o;
};
int main() {
Kid<2>{};
}
Run Code Online (Sandbox Code Playgroud)
[GCC]编译代码而没有任何问题,[铛]抱怨匹配Parent
针对Kid
问题:
prog.cc:7:15: note: candidate template ignored: could not match 'Parent' against 'Kid'
constexpr int operator*(A, Parent<N>*) { return N; } …
Run Code Online (Sandbox Code Playgroud) c++ language-lawyer incomplete-type constexpr template-argument-deduction