函数只接受非常量左值

时间:2015-09-22 15:20:12

标签: c++ c++11 vector c++14 universal-reference

我有一个函数可以对两个向量进行排序,其中第一个向量作为排序标准。它的签名是

template<typename A, typename B>
void sort(A&& X, B&& Y)
{
  ..
}

问题是普遍引用会允许像

这样的无意义案例
sort(vector<int>{ 2,1,3 }, vector<int>{ 3,1,2 });

其后rvalue将被销毁(废话)。

明确询问左值是不行的,因为

template<typename A, typename B>
void sort(A& X, B& Y) ... // (*)

sort(vector<int>{2,1,3}, vector<int>{3,1,2});

由于某种原因上面的编译(我认为只允许const值左右绑定到rvalues并延长它们的生命周期?)。

如果我将const添加到左值参考,那么该函数将无法再修改向量并对其进行排序。

我的问题是:

1)为什么在标有// (*)的示例中,我可以将rvalue绑定到甚至不是const的左值?为什么不允许int& r = 20;之类的内容?有什么区别?

2)我如何解决我的问题,即让函数接受左值而不是右值临时值? (如果可能,当然)

显然我可以使用任何可用的C ++版本

5 个答案:

答案 0 :(得分:7)

答案是:你的编译器错了。

检查gcc或clang或类似内容,你会得到这样的结果:

  

prog.cpp:在函数'int main()'中:prog.cpp:9:45:错误:无效   初始化'std :: vector&amp;'类型的非const引用从   'std :: vector'类型的右值(向量{2,1,3},   向量{3,1,2});                                                ^ prog.cpp:6:6:注意:初始化'void sort(A&amp;,B&amp;)的参数1 [用A =   的std ::向量; B = std :: vector]'void sort(A&amp; X,B&amp; Y){}

答案 1 :(得分:2)

您可以使用/Za编译器选项将其变为错误:

error C2664: 'void sort<std::vector<int,std::allocator<_Ty>>,std::vector<_Ty,std::allocator<_Ty>>>(A &,B &)' : cannot convert argument 1
from 'std::vector<int,std::allocator<_Ty>>' to 'std::vector<int,std::allocator<_Ty>> &'
        with
        [
            _Ty=int
,            A=std::vector<int,std::allocator<int>>
,            B=std::vector<int,std::allocator<int>>
        ]
        and
        [
            _Ty=int
        ]
        and
        [
            _Ty=int
        ]
        A non-const reference may only be bound to an lvalue

请注意/Za过去遇到了一些问题,甚至现在仍然会中断<windows.h>,因此无论如何都不能将它用于所有编译单元。在2012年标题为"MSVC /Za considered harmful"的帖子中,微软高级工程师Stephan T. Lavavej甚至建议不要使用该标志,但您还应该查看STL Fixes In VS 2015, Part 2处的评论,他说:

  

我们肯定有关于/Za/Zc一致性的会议   选项。我们最终希望达到VC符合要求的程度   默认情况下,无需请求额外选项,因此成为   最常用和最受测试的路径。正如你在帖子中看到的那样,我是   通过删除非标准,在STL中努力实现这一目标   机器尽可能。

因此,在某些未来版本的MSVC中,默认情况下这可能是编译错误。

另一件事:C ++标准不区分错误和警告,它只讨论&#34;诊断消息&#34;。这意味着MSVC实际上符合一旦产生警告就会符合。

答案 2 :(得分:1)

正如其他答案所述,编译器错了。

无需更改编译器选项的编译器:

struct sfinae_helper {};
template<bool b>
using sfinae = typename std::enable_if<b, sfinae_helper>::type*;
// sfinae_helper, because standard is dumb: void*s are illegal here

template<class A, class B,
  sfinae<!std::is_const<A>::value&&!std::is_const<B>::value> = nullptr
>
void sort(A& X, B& Y) ... // (*)

sort(vector<int>{2,1,3}, vector<int>{3,1,2});

也无法在MSVC2013中编译,并且应该符合兼容的编译器。

请注意,虽然根据标准推断AB const X不合法,但明确将const X传递为AB

最后一种方法是:

template<typename A, typename B>
void sort(A& X, B& Y) ... // (*)
template<typename A, typename B>
void sort(A&& X, B&& Y) = delete;

我们生成一个明确删除的应该优先于A&, B&的那个。我不知道MSVC在这种情况下是否正确地选择了完美转发的那个,但我希望如此。

答案 3 :(得分:1)

作为对X问题的回答,你试图解决而不是你问的Y问题......正确的答案是你不应该做你想做的事情。无法想象某些事物是如何有用的并不是一个充分的理由来阻止人们做到这一点。

事实上,我甚至不必在摘要中提出这个建议:这里有两个具体的例子,其中接受临时对象会很有用。

您可能只关心两个对象中的一个:

interesting_container A;
// fill A
sort(an_ordering_criterion(), A);

容器不是自包含的;&#39;例如一个容器,提供另一个视图:

vector<int> A, B;
// fill A and B
sort(make_subsequence(A, 1, 10), make_subsequence(B, 5, 14));

答案 4 :(得分:0)

您可以明确delete sort函数的意外重载:

#include <iostream>
#include <vector>

#include <cstdlib>

template< typename X, typename Y >
void
sort(X &, Y &)
{
    static_assert(!std::is_const< X >{});
    static_assert(!std::is_const< Y >{});
}

template< typename X, typename Y >
int
sort(X const &, Y &) = delete;

template< typename X, typename Y >
int
sort(X &, Y const &) = delete;

template< typename X, typename Y >
int
sort(X const &, Y const &) = delete;

int
main()
{
    std::vector< int > v{1, 3, 5};
    std::vector< int > const c{2, 4, 6};
    ::sort(v, v); // valid
    { // has been explicitly deleted
        //::sort(v, c); 
        //::sort(c, v);
        //::sort(c, c);
    }
    { // not viable: expects an l-value for 1st argument
        //::sort(std::move(v), v); 
        //::sort(std::move(v), c);
        //::sort(std::move(c), v);
        //::sort(std::move(c), c);
    }
    { // not viable: expects an l-value for 2nd argument
        //::sort(v, std::move(v));
        //::sort(v, std::move(c));
        //::sort(c, std::move(v));
        //::sort(c, std::move(c));
    }
    { // not viable: expects an l-value for 1st or 2nd argument
        //::sort(std::move(v), std::move(v));
        //::sort(std::move(v), std::move(c));
        //::sort(std::move(c), std::move(v));
        //::sort(std::move(c), std::move(c));
    }
    return EXIT_SUCCESS;
}