下面的代码来自 libstdc++-v3 std::type_traits,它是一个实现std::declval:
template<typename _Tp, typename _Up = _Tp&&> // template 1
_Up
__declval(int);
template<typename _Tp> // template 2
_Tp
__declval(long);
template<typename _Tp> // template 3
auto declval() noexcept -> decltype(__declval<_Tp>(0));
Run Code Online (Sandbox Code Playgroud)
但我认为我可以declval简单地实现:
template <typename T> T declval();
Run Code Online (Sandbox Code Playgroud)
这是我的测试代码:
#include <iostream>
using namespace std;
struct C {
C() = delete;
int foo() { return 0; }
};
namespace test {
template <typename T> T declval();
};// namespace test
int main() {
decltype(test::declval<C>().foo()) n = 1;
cout …Run Code Online (Sandbox Code Playgroud) 使用c ++ 17,我们看到了新的is_invocable和花哨的新prvalues,它们并不是真正的价值.
这允许您创建一个对象而无需首先在逻辑上构造它,然后忽略构造.
我遇到了一个问题,std::is_invocable用于测试你是否可以调用某些内容,而prvalue规则似乎会发生冲突:
struct no_move {
no_move(no_move&&)=delete;
explicit no_move(int) {}
};
void f( no_move ) {}
Run Code Online (Sandbox Code Playgroud)
现在我们可以问一下是否f可以使用类型的prvalue调用no_move?
f( no_move(1) )
Run Code Online (Sandbox Code Playgroud)
std::is_invocable< decltype(&f), no_move >不起作用,因为它使用的std::declval<no_move>()是xvalue,no_move&&而不是类型的prvalue no_move.
在c ++ 14中,这是相同的,但保证省略使得一些函数可以用xvalue(即" T&&")调用,而其他函数可以用prvalues类型调用T.
有替代方案,还是我们必须发明自己的特性来处理这种情况?
(在一个理论的世界里std::declval<T>返回T,而不是T&&,is_invocable会的,我相信,做正确的事).
查看libstdc ++源代码,我发现以下declval实现:
template<typename _Tp, typename _Up = _Tp&&>
_Up __declval(int); // (1)
template<typename _Tp>
_Tp __declval(long); // (2)
template<typename _Tp>
auto declval() noexcept -> decltype(__declval<_Tp>(0));
Run Code Online (Sandbox Code Playgroud)
此实现是 Eric Niebler 提出的,用于优化编译时间:他解释说,重载解析比模板实例化更快。
但是,我不明白它是如何工作的。特别:
_Up不仅仅是返回_Tp&&?与最幼稚的实现相反,这一切如何防止模板实例化:
template<typename T>
T&& declval() noexcept;
Run Code Online (Sandbox Code Playgroud) 我正在尝试做这样的事情(在c ++ 11中):
#include <utility>
template <typename T>
struct base {
using type = decltype( std::declval<T>().foo() );
};
struct bar : base<bar> {
int foo() { return 42;}
};
int main() {
bar::type x;
}
Run Code Online (Sandbox Code Playgroud)
失败与
prog.cc: In instantiation of 'struct base<bar>':
prog.cc:8:14: required from here
prog.cc:5:46: error: invalid use of incomplete type 'struct bar'
using type = decltype( std::declval<T>().foo() );
~~~~~~~~~~~~~~~~~~^~~
prog.cc:8:8: note: forward declaration of 'struct bar'
struct bar : base<bar> {
^~~
Run Code Online (Sandbox Code Playgroud)
如何声明bar::fooin 的返回类型的别名base …
考虑这个例子(来自这里):
#include <type_traits>
#include <iostream>
template <typename U>
struct A {
};
struct B {
template <typename F = int>
A<F> f() { return A<F>{}; }
using default_return_type = decltype(std::declval<B>().f());
};
int main()
{
B::default_return_type x{};
std::cout << std::is_same< B::default_return_type, A<int>>::value;
}
Run Code Online (Sandbox Code Playgroud)
它在 gcc9.2 上编译没有错误,但 gcc7.2 和 clang 10.0.0 抱怨B不完整。Clangs 错误是:
prog.cc:11:58: error: member access into incomplete type 'B'
using default_return_type = decltype(std::declval<B>().f());
^
prog.cc:7:8: note: definition of 'B' is not complete until the …Run Code Online (Sandbox Code Playgroud) 我一直试图从这里std::reference_wrapper理解 的实现,如下:
namespace detail {
template <class T> constexpr T& FUN(T& t) noexcept { return t; }
template <class T> void FUN(T&&) = delete;
}
template <class T>
class reference_wrapper {
public:
// types
typedef T type;
// construct/copy/destroy
template <class U, class = decltype(
detail::FUN<T>(std::declval<U>()),
std::enable_if_t<!std::is_same_v<reference_wrapper, std::remove_cvref_t<U>>>()
)>
constexpr reference_wrapper(U&& u) noexcept(noexcept(detail::FUN<T>(std::forward<U>(u))))
: _ptr(std::addressof(detail::FUN<T>(std::forward<U>(u)))) {}
reference_wrapper(const reference_wrapper&) noexcept = default;
// assignment
reference_wrapper& operator=(const reference_wrapper& x) noexcept = default;
// access
constexpr operator T& () const …Run Code Online (Sandbox Code Playgroud) 下面的代码或在godbolt上的代码可以使用 gcc 和 MSVC 进行编译,但会因 clang 失败。我找不到标准中是否/哪里禁止它。我认为应该支持。
那么,clang 和 gcc/MSVC 谁的说法是正确的呢?
#include <type_traits>
void foo() {
static_assert(decltype([_=std::declval<int>()]() consteval noexcept { // clang error: declval() must not be used
if constexpr (std::is_integral<decltype(_)>::value) {
return std::bool_constant<true>();
} else {
return std::bool_constant<false>();
}
}())::value);
}
Run Code Online (Sandbox Code Playgroud)
该示例可以扩展为以下 3 种情况或在godbolt上:
因此,很明显,它在 lambda 主体中不合法,但在外部作为调用者参数是合法的。目前尚不清楚捕获列表中是否允许。
#include <type_traits>
auto foo_lambda_argument() {
return decltype([](auto _) noexcept {
return std::bool_constant<std::is_integral<decltype(_)>::value>();
}(std::declval<int>()))::value; // OK …Run Code Online (Sandbox Code Playgroud) 我看到一些代码示例,其中用于实例化模板函数的类型std::declval被指定为引用类型而不仅仅是类型,如下所示:
std::declval<T &>()
Run Code Online (Sandbox Code Playgroud)
与以下相反:
std::declval<T>()
Run Code Online (Sandbox Code Playgroud)
T某种类型在哪里。我忽略了为什么可以选择引用符号而不是普通类型的微妙之处。有人可以向我解释一下吗?
我知道它std::declval会扩展为,typename std::add_rvalue_reference<T>::type但我仍然不明白为什么人们会通过引用类型而不是普通类型本身来实例化后者。
我很惊讶地发现,对于某些人T,这decltype(std::declval<T>())是不合法的:
#include <utility>
template<typename T>
using Alias = decltype(std::declval<T>());
// as expected
using A1 = Alias<int>;
using A2 = Alias<int(int)>;
// error: no matching function for call to 'declval<...>()'
using A3 = Alias<int(int) const>;
using A4 = Alias<int(int) volatile>;
using A5 = Alias<int(int) &>;
using A6 = Alias<int(int) &&>;
// and all combinations of the above
Run Code Online (Sandbox Code Playgroud)
cppreference似乎并不表明该错误是预期的。
还有其他declval<T>不能使用的类型吗?规范在哪里定义这些?
标准库实用程序declval被定义为:
template<class T> add_rvalue_reference_t<T> declval() noexcept;
Run Code Online (Sandbox Code Playgroud)
在这里添加右值引用似乎是一个好主意,如果您考虑一下C++11 中引入的语言:返回值涉及一个临时值,该值随后被移出。现在C++17引入了保证复制省略,这不再适用。正如cppref所说:
纯右值和临时值的 C++17 核心语言规范与早期 C++ 修订版的核心语言规范根本不同:不再有临时值可以复制/移动。描述 C++17 机制的另一种方法是“非物化值传递”:返回和使用纯右值,而不会物化临时值。
这对其他以declval. 看看这个例子(在Godbolt.org上查看):
#include <type_traits>
struct Class {
explicit Class() noexcept {}
Class& operator=(Class&&) noexcept = delete;
};
Class getClass() {
return Class();
}
void test() noexcept {
Class c{getClass()}; // succeeds in C++17 because of guaranteed copy elision
}
static_assert(std::is_constructible<Class, Class>::value); // fails …Run Code Online (Sandbox Code Playgroud) template<typename T>
class A
{
public:
A(T &t)
: t_(t){}
T t_;
};
int main()
{
int value;
A<decltype(value)> a(value);
// what I wish for : A a(value);
// which does not compile "missing template argument before 'a'"
}
Run Code Online (Sandbox Code Playgroud)
在 A (或其他地方)的声明中有没有办法提示编译器 T 应该自动解析为传递给构造函数的类型?
(理想情况下是 c++11,但很高兴听到不那么旧的版本)
在cppreference-page上有std::declval以下内容:
返回值
无法调用,因此永远不会返回值。
这是什么意思?我们在使用的时候肯定会调用它吗?
struct Foo { int func(){} };
decltype(std::declval<Foo>().func()) integer;
/* ^
| */
Run Code Online (Sandbox Code Playgroud)