【问题标题】:Having a function only accept non-const lvalues有一个函数只接受非常量左值
【发布时间】:2015-12-19 15:52:00
【问题描述】:

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

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

问题是通用引用会允许像这样的无意义的情况

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

其中一个右值将在之后被销毁(废话)。

明确要求左值不起作用,因为

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

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

出于某种原因,上述编译(我认为只有 const lvalues 被允许绑定到 rvalues 并延长它们的生命周期?)。

如果我将const 添加到左值引用,则该函数将不再能够修改向量并对其进行排序。


我的问题是:

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

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

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

【问题讨论】:

  • 我怀疑 Visual Studio 编译器...
  • 正如@BeyelerStudios 所暗示的,Visual Studio 编译器已经支持将临时对象绑定到非常量引用有一段时间了(不幸的是)。
  • 但是,至少如果您使用足够高的警告级别,它确实会发出警告 - 您可能应该这样做
  • 具体来说,MSVC 2013 将给出“mysourcefile.cpp(10): 警告 C4239: nonstandard extension used : 'argument' : conversion from '' to '' 1 > 非 const 引用只能绑定到左值”,仅在警告级别 /W4 或更高级别。
  • @jaggedSpire 好资料

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


【解决方案1】:

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

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

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

【讨论】:

  • 为什么编译器不推导出类型A = const vector&lt;int&gt;?我的意思是我可以显式调用split&lt;const vector&lt;int&gt;, const vector&lt;int&gt; &gt;(vector&lt;int&gt;{2,1,3}, vector&lt;int&gt;{3,1,2}),编译器会很乐意接受我的临时变量。我能找到的唯一远引用是:“如果 P 是 cv 限定类型,则忽略顶级 cv 限定符进行推导。”来自en.cppreference.com/w/cpp/language/template_argument_deduction
  • @whY 因为对于右值,const 引用与模板推导不完全匹配。您可以定义 const vector&lt;int&gt; a{2, 1, 3}, b{3, 1, 2}; 然后 a, b 是左值,因此 const 引用将完全匹配或自己实例化模板以将右值绑定到 const 引用。比较 stackoverflow.com/q/11931452stackoverflow.com/q/6075093
【解决方案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 过去曾有过不少问题,即使现在仍然会破坏&lt;windows.h&gt;,因此无论如何您都不能将它用于所有编译单元。在 2012 年题为 "MSVC /Za considered harmful" 的帖子中,微软高级工程师 Stephan T. Lavavej 甚至建议不要使用该标志,但您还应该查看STL Fixes In VS 2015, Part 2 的 cmets,他说:

我们肯定已经就/Za/Zc 的一致性举行过会议 选项。我们最终希望达到 VC 符合要求的地步 默认情况下,无需请求额外的选项,这样就变成了 最常用和测试最多的路径。正如你在帖子中看到的,我已经 在 STL 中通过删除 non-Standard 来实现这一点 尽可能使用机器。

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


另一件事:C++ 标准不区分错误和警告,它只谈论“诊断消息”。这意味着 MSVC 实际上 在产生警告后立即符合要求。

【讨论】:

  • 希望 STL 不再称它为 STL
  • @LightnessRacesinOrbit:我知道,我知道......另一方面,像这样的名字,我会说他应该被原谅。
  • @LightnessRacesinOrbit 似乎 Herb 和 Bjarne 正式将标准库命名为 STL(参见 cppcore repository)。所以我想从现在开始 STL = STandard Library
  • @Dean:Herb 和 Bjarne 都无权定义官方命名。只有标准 ISO 文件中的内容才能被视为官方文件。
  • @ChristianHackl:我反驳说,来自同名的人更糟糕:P
【解决方案3】:

正如其他答案所指出的,编译器是错误的。

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

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&amp;, B&amp; 的。我不知道 MSVC 在这种情况下是否正确选择了完美转发的那个,但我希望如此。

【讨论】:

  • sfinae_helper 示例在 MSVC 2013 中编译时不会出现错误和警告(除非使用 /W4/Za)。
  • 是的,完全没有错误。除非我把它变成一段完整的代码时搞砸了(例如,将调用放入main,添加&lt;vector&gt;&lt;type_traits&gt; 包括)。
  • @chris 我想知道“将右值绑定到左值”是否正在发生,我将其误解为类型推导问题。
【解决方案4】:

作为您尝试解决的 X 问题而不是您提出的 Y 问题的答案...正确的答案是您不应该做您想做的事情。无法想象某样东西有什么用处,并不是竭尽全力阻止人们使用它的充分理由。

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

您可能只关心以下两个对象之一:

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

容器不是“独立的”;例如提供另一个视图的容器:

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

【讨论】:

    【解决方案5】:

    您可以显式地 deletesort 函数进行不希望的重载:

    #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;
    }
    

    【讨论】:

      猜你喜欢
      • 2014-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-05
      • 1970-01-01
      • 2013-07-12
      • 1970-01-01
      相关资源
      最近更新 更多