当前位置: 首页 > article >正文

过年之无用知识研究:std::is_assignable中的declval<_Dest>() = declval<_Src>()

 std::pair的默认operator=被delete掉了,取而代之的是两个enable_if版本。

为什么这么设计,我的理解是pair作为左值时,里面的first如果是const,那么就不允许了。比如,在std::map里,已经保存的元素的key值是不能被修改的,这一点会在编译时报出错误。比如

注意,下面的代码会修改key值,编译时出现错误:
两个enable_if版本推导失败,最后落到了:pair& operator=(const volatile pair&) = delete;
这个版本,但这个版本又被删除了。所以出现了编译错误。

{
        std::map<int, int> m;
        //初始化
        auto it = m.begin();
        *it = std::make_pair(1, 1); //问题就出在这里
}

就会提示:
error C2679: 二进制“=”: 没有找到接受“std::pair<int,int>”类型的右操作数的运算符(或没有可接受的转换)
1>d:\devtools\vs2017\vc\tools\msvc\14.16.27023\include\utility(276): note: 可能是“std::pair<const _Kty,_Ty> &std::pair<const _Kty,_Ty>::operator =(volatile const std::pair<const _Kty,_Ty> &)”
1>        with
1>        [
1>            _Kty=int,
1>            _Ty=int
1>        ]
1>c:\work\hchx\ceripipe\codestudy\consoleapplication2\consoleapplication1\consoleapplication1.cpp(2114): note: 尝试匹配参数列表“(std::pair<const _Kty,_Ty>, std::pair<int,int>)”时
1>        with
1>        [
1>            _Kty=int,
1>            _Ty=int
1>        ]

代码:
std::pair<int, int> data0 = std::make_pair(1, 2);
std::pair<const int, int> data1 = std::make_pair(3, 4);
data0 = data1;

堆栈中可以看到推导结果:
1.exe!std::pair<int,int>::operator=<int const ,int,0>
(const std::pair<int const ,int> & _Right={...}) 行 288	C++

推导出的符号,
_Ty1和_Other1类型是:const int
_Ty2和_Other2类型是:int

查看pair的operator=的enable_if不分,
is_assignable<_Ty1&, const _Other1&>怎么算,替换下来就是:
is_assignable<const int&, const const int&>
看到const const int&,居然有两个const,能行吗?考虑到is_asignable的实现,也就是相当于,
{
        using T = decltype(declval<int&>() = declval<const const int&>());
        int b = 0;
        T a = b;
        std::ignore = a;
}
vs2015下编译成功。神奇。
而is_assignable<_Ty1&, const _Other1&>就是:is_assignable<int&, const int&>
{
        using T = decltype(declval<int&>() = declval<const int&>());
        int b = 0;
        T a = b;
        std::ignore = a;
}
vs2015下也能编译成功。

std::cout << boost::typeindex::type_id_with_cvr<const const int&>().pretty_name() << std::endl;
打印结果是:int const & __ptr64

所以,enable_if_t<conjunction_v<
			is_assignable<_Ty1&, const _Other1&>,
			is_assignable<_Ty2&, const _Other2&>
		>, int>推导成功,
相当于:
   enable_if_t<conjunction_v<true,true>, int> 
-> enable_if_t<true, int> 
-> int

所以才走到了下面的operator=的版本。


结合源码:
	pair& operator=(const volatile pair&) = delete; //默认的版本已被删除

	template<class _Other1 = _Ty1, //_Ty1:const int, _Other1: const int
		     class _Other2 = _Ty2, //_Ty2:int      , _Other2: int
		enable_if_t<conjunction_v<
			is_assignable<const int&, const const int&>,
			is_assignable<int&      , const int&>
		>, int> = 0>
		pair& operator=(const pair<const int, int>& _Right)
			_NOEXCEPT_COND(is_nothrow_assignable_v<_Ty1&, const _Other1&>
				&& is_nothrow_assignable_v<_Ty2&, const _Other2&>)	// strengthened
		{
		first = _Right.first;
		second = _Right.second;
		return (*this);
		}

How to interpret declval<_Dest>() = declval<_Src>() in is_assignable

I am trying to figure out how to interpret declval<_Dest>() = declval<_Src>() in the implementation of is_assignable.

declval turns a type into a reference. Given that, I translate the expression into one of the following four possibilities:

  1. _Dest&& = _Src&&
  2. _Dest&& = _Src&
  3. _Dest& = _Src&&
  4. _Dest& = _Src&

I then created two helper functions.

template <typename T> T rvalue();
template <typename T> T& lvalue();

My understanding is the four expressions can be realized by using the template functions.

  1. _Dest&& = _Src&& -----> rvalue<_Dest>() = rvalue<_Src>()

Same goes for the other three.

Then I simulated decltype(declval<_Dest>() = declval<_Src>(), ..) by compiling the templated function version of each of the possibilities for three pairs of concrete types.

  • _Dest=int, _Src=int. Compiler accepts #3 and #4. is_assignable returned true for #3 and #4. They agreed.
  • _Dest=int, _Src=double. Same result as
  • _Dest=double, _Src=int. For this one, the compiler and is_assignable didn't agree. Compiler again does not like assigning to rvalues. However, is_assignable returns true for all four possibilities.

My questions are

  • Did I interpret declval<_Dest>() = declval<_Src>() correctly? In order words, does this really translate into the four possibilities. If yes, can each one be mapped to a templated function expression?
  • Why the compiler and is_assignable disagree on the _Dest=double, _Src=int case?

Thanks.

  • c++
  • c++11

Share

Improve this question

Follow

edited Dec 6, 2013 at 5:34

ildjarn

63k99 gold badges131131 silver badges216216 bronze badges

asked Dec 6, 2013 at 2:35

Candy Chiu

6,67999 gold badges5151 silver badges7070 bronze badges

  • 1

    Which compiler is "the" compiler? At least one compiler appears to work precisely as you expect.

    – Igor Tandetnik

     Commented Dec 6, 2013 at 3:18 
  • The compiler is VC++2013

    – Candy Chiu

     Commented Dec 6, 2013 at 13:21 
https://ideone.com/QdRjZB

#include <iostream>
#include <type_traits>
using namespace std;
 
template <typename T>
T rvalue() { return T(); }
 
template <typename T>
T& lvalue() { static T t; return t; }
 
int main() {
	cout << is_assignable<double, int>::value;
	cout << is_assignable<double, int&>::value;
	cout << is_assignable<double&, int>::value;
	cout << is_assignable<double&, int&>::value;
 
    // rvalue<double>() = rvalue<int>(); // Doesn't compile
    // rvalue<double>() = lvalue<int>(); // Doesn't compile
    lvalue<double>() = rvalue<int>(); // OK
    lvalue<double>() = lvalue<int>(); // OK
	return 0;
}

一个回答:

std::declval is actually specified to be (C++11 §20.2.4 [declval] p1):

template <class T>
typename add_rvalue_reference<T>::type declval() noexcept;

The result of the reference collapsing rules (§8.3.2 [dcl.ref] p6) is that declval returns an lvalue reference when T is an lvalue reference type, and an rvalue reference otherwise. So yes, your interpretation is correct.

If your compiler thinks that double&& is assignable from any type, then it has a bug. §5.17 [expr.ass] p1 states:

The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand and return an lvalue referring to the left operand.

[emphasis mine].

Many programmers choose to emulate this behavior - assingment only to lvalues - with their own types by declaring the assignment operators with an lvalue reference qualifier:

class foo {
  foo& operator = (const foo&) & = default;
  foo& operator = (foo&&) & = default;
};

Understanding how declval is working in the copy_assignment situation

I was watching Dr. Walter E. Brown's Template Meta-programming talk. In his presentation he presents code like so for is_copy_assignable:

template<class U, class = decltype(declval<U&>() = declval<U const&>())>
static true_type try_assignment(U &&);

What I am having trouble with is how is the assignment operator being called in this instance. When I try and reason about this code and say substitute a dummy type, say int, in place of the U I get:

template<class U, class = decltype(declval<int&>() = declval<int const&>())> template<class U, class = decltype(int& = int const&)>

So I am wondering then how can this help us determine if the assignment operator is valid. If I understand declval correctly this code will not even evaluate so then how can one determine from int& = int const&, which doesn't even evaluate, if there is or isn't a assignment operator defined for U.

I understand that in most cases the copy-assignment operator would be defined as

C& operator=(const C& other)

which looks a lot like what is above, but still since nothing is being evaluated then what use is this information.

  • c++
  • c++11

Share

Improve this question

Follow

edited Feb 28, 2018 at 1:02

Lightness Races in Orbit

385k7777 gold badges664664 silver badges1.1k1.1k bronze badges

asked Feb 5, 2018 at 2:21

cogle

1,06911 gold badge1414 silver badges2626 bronze badges

  • Why did you turn declval<int&>() into int&? What do you think declval<int&>() does?

    – Mooing Duck

     Commented Feb 28, 2018 at 1:06 

Add a comment

1 Answer

Sorted by:

1

I don't really follow what you intended in performing the following step:

template<class U, class = decltype(declval<int&>() = declval<int const&>())>
template<class U, class = decltype(int& = int const&)>

declval says: pretend to return a value of type of the template argument. I say pretend because the function isn't really defined, only declared, so it can only be used in an unevaluated context. Inside of decltype is an unevaluated contexts because you're only checking types.

So, the expression decltype(declval<int&>() = declval<int const&>()) is basically saying: if I had an int&, call it x, and I had a int const&, call it y, what is the type of the expression x = y?

As you can probably guess this calls the assignment operator and this expression will have a valid type (in this case, int&). If you change int to unique_ptr, this expression won't have a valid type, because unique_ptr is not assignable, causing type substitution to fail and eliminating this template from the overload or specialization set.

The reason to have declval at all is because it lets you create a value of any type; this is particularly useful for a) creating something of reference type, and b) creating non-reference types without assuming that they have e.g. a default constructor. Thus, use of declval is extremely ubiquitous in high quality TMP.

Share

Improve this answer

Follow

answered Feb 5, 2018 at 2:47

  • 2

    Thanks, I was misunderstanding what declval actually was doing. Its pretending to return the value which of the type which is what I wasn't picking up on.

    – cogle

     Commented Feb 5, 2018 at 3:14 


http://www.kler.cn/a/524220.html

相关文章:

  • Linux pkill 命令使用详解
  • 本地部署deepseek模型步骤
  • 推动知识共享的在线知识库实施与优化指南
  • 17 一个高并发的系统架构如何设计
  • 简要介绍C语言和c++的共有变量,以及c++特有的变量
  • 【C语言练习题】找出不是两个数组共有的元素
  • Spring Boot 高级开发指南:全面掌握微服务架构的关键技术
  • Java中的反射机制:深入理解getConstructor(Class<?>... parameterTypes)方法
  • RocketMQ事务消息是如何实现的?
  • python爬虫验证下载的图片是否损坏方法
  • lib.exe正确用法winhv.lib生成方法
  • 题解:P10972 I-Country
  • Swift 中 Codable 和 Hashable 的理解
  • < OS 有关> BaiduPCS-Go 程序的 菜单脚本 Script: BaiduPCS-Go.Menu.sh (bdgo.sh)
  • 基于 STM32 的智能工业水质监测与净化系统
  • scrol家族 offset家族 client家族学习
  • js学习笔记(2)
  • 单链表专题(上)
  • 玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
  • 19.Word:小马-校园科技文化节❗【36】
  • QT+mysql+python 效果:
  • 八种排序算法【C语言实现】
  • 代码随想录| 动态规划188.买卖股票的最佳时机IV 309.最佳买卖股票时机含冷冻期 714.买卖股票的最佳时机含手续费
  • 技术发展视域下中西方技术研发思维方式的比较与启示
  • 传奇引擎游戏微端的作用
  • 5分钟带你获取deepseek api并搭建简易问答应用