是否可以通过const引用获取参数,同时禁止转换以便不传递临时值?

时间:2012-01-25 15:02:26

标签: c++ const

有时我们喜欢通过引用获取一个大参数,并且如果可能的话也使参考const宣称它是一个输入参数。但是通过制作引用const,编译器然后允许自己转换数据,如果它是错误的类型。这意味着它不那么有效,但更令人担心的是我认为我指的是原始数据;也许我会接受它的地址,而不是意识到我实际上是取了一个临时的地址。

此代码中对bar的调用失败。这是可取的,因为引用的类型不正确。对bar_const的调用也是错误的类型,但它默默地编译。这对我来说是不受欢迎的。

#include<vector>
using namespace std;

int vi;

void foo(int &) { }
void bar(long &) { }
void bar_const(const long &) { }

int main() {
   foo(vi);
   // bar(vi); // compiler error, as expected/desired
   bar_const(vi);
}

传递轻量级只读引用最安全的方法是什么?我很想创建一个类似于参考的新模板。

(显然,intlong是非常小的类型。但是我已经遇到了可以相互转换的更大的结构。我不希望这种情况在我发生时默默发生采用const引用。有时,将构造函数标记为显式有用,但这并不理想)

更新:我想象一个如下系统:想象一下有两个函数X byVal();X& byRef();以及下面的代码块:

 X x;
 const_lvalue_ref<X> a = x; // I want this to compile
 const_lvalue_ref<X> b = byVal(); // I want this to fail at compile time
 const_lvalue_ref<X> c = byRef(); // I want this to compile

该示例基于局部变量,但我希望它也可以使用参数。我希望得到某种错误消息,如果我不小心将ref-to-temporary或ref-to-a-copy传递给我认为我会传递一些轻量级的内容,例如ref-to-lvalue。这只是一个“编码标准”的东西 - 如果我真的想允许将ref传递给临时的,那么我将使用简单的const X&。 (我发现this piece on Boost's FOREACH非常有用。)

6 个答案:

答案 0 :(得分:7)

好吧,如果你的“大参数”是一个类,首先要做的是确保你明确标记任何单个参数构造函数(除了复制构造函数):

class BigType
{
public:
    explicit BigType(int);
};

这适用于具有可能使用单个参数调用的默认参数的构造函数。

然后它不会自动转换为,因为编译器没有用于进行转换的隐式构造函数。你可能没有任何构成该类型的全局转换运算符,但如果你这样做,那么

如果这对您不起作用,您可以使用一些模板魔法,例如:

template <typename T>
void func(const T &); // causes an undefined reference at link time.

template <>
void func(const BigType &v)
{
    // use v.
}

答案 1 :(得分:3)

如果您可以使用C ++ 11(或其中的一部分),这很容易:

void f(BigObject const& bo){
  // ...
}

void f(BigObject&&) = delete; // or just undefined

Live example on Ideone

这将起作用,因为绑定到rvalue ref优先于绑定到临时对象的引用-const。

您还可以利用隐式转换序列中仅允许单个用户定义转换的事实:

struct BigObjWrapper{
  BigObjWrapper(BigObject const& o)
    : object(o) {}

  BigObject const& object;
};

void f(BigObjWrapper wrap){
  BigObject const& bo = wrap.object;
  // ...
}

Live example on Ideone

答案 2 :(得分:2)

这很容易解决:停止通过引用获取值。如果您想确保参数可寻址,请将其设为地址

void bar_const(const long *) { }

这样,用户必须传递指针。并且您无法获得指向临时的指针(除非用户非常恶意)。

话虽这么说,我认为你对这件事的看法是......错误的。它归结为这一点。

  

也许我会接受它的地址,而不是意识到我实际上是在接受临时的地址。

考虑恰好是临时的const&的地址实际上是好的。问题是你不能长期存储它。你也不能转让它的所有权。毕竟,您有一个 const 参考。

这就是问题的一部分。如果您选择const&,您的界面会说:“我被允许使用此对象,但我不拥有它,也不能将所有权授予其他人。”由于您不拥有该对象,因此无法长期存储该对象。这就是const&的含义。

取一个const*可能会有问题。为什么?因为你不知道那个指针来自哪里。谁拥有这个指针? const&有许多语法保护措施可以防止你做坏事(只要你不接受它的地址)。 const*什么都没有;你可以将指针复制到心脏的内容。您的界面没有说明您是否被允许拥有该对象或将所有权转让给他人。

这种含糊不清是C ++ 11具有unique_ptrshared_ptr等智能指针的原因。这些指针可以描述真正的内存所有权关系。

如果您的函数的值为unique_ptr,那么您现在拥有该对象。如果需要shared_ptr,那么您现在分享该对象的所有权。有确保所有权的语法保证(再次,除非你采取不愉快的步骤)。

如果你不使用C ++ 11,你应该使用Boost智能指针来实现类似的效果。

答案 3 :(得分:1)

你不能,即使你可以,它可能也没有多大帮助。 考虑:

void another(long const& l)
{
    bar_const(l);
}

即使你可以以某种方式阻止绑定到临时输入 bar_const,可以使用引用调用another之类的函数 绑定到一个临时的,你最终会遇到同样的情况。

如果您不能接受临时,则需要使用对a的引用 非const或指针:

void bar_const(long const* l);

需要左值来初始化它。当然,像

这样的功能
void another(long const& l)
{
    bar_const(&l);
}

仍然会导致问题。但是如果你全球采用这个惯例 如果对象生存期必须延伸到调用结束之外,则使用指针, 然后希望another的作者会考虑他为什么要这么做 地址,并避免它。

答案 4 :(得分:0)

我认为你的intlong的例子有点像规范的C ++,你永远不会通过const引用传递内置类型:你通过值或非传递它们const参考。

因此,我们假设您有一个大的用户定义类。在这种情况下,如果它正在为您创建临时值,那么这意味着您为该类创建了隐式转换。您所要做的就是将所有转换构造函数(可以使用单个参数调用的构造函数)标记为explicit,编译器将阻止自动创建这些临时值。例如:

class Foo
{
    explicit Foo(int bar) { }
};

答案 5 :(得分:0)

(在我提出的另一个问题上,感谢this great answer回答我自己的问题。感谢@hvd。)

简而言之,将函数参数标记为volatile意味着它不能绑定到右值。 (任何人都可以为此标准引用吗?Temporaries可以绑定到const&,但显然不会绑定到const volatile &。这就是我对g ++ - 4.6.1的看法。(额外:见{{ 3}}对于我头上方式的一些血腥细节:-)))

void foo( const volatile Input & input, Output & output) {
}

foo(input, output); // compiles. good
foo(get_input_as_value(), output); // compile failure, as desired.

但是,您实际上希望参数为volatile。所以我给const_cast volatile写了一个小包装器。所以foo的签名就变成了这个:

void foo( const_lvalue<Input> input, Output & output) {
}

包装器是:

template<typename T>
struct const_lvalue {
    const T * t;
    const_lvalue(const volatile T & t_) : t(const_cast<const T*>(&t_)) {}
    const T* operator-> () const { return t; }
};

这可以仅从左值

创建

有任何缺点吗?这可能意味着我不小心误用了一个真正不稳定的物体,但在我的生命中,我再也没有使用volatile。我认为这对我来说是正确的解决方案。

我希望能够养成默认使用所有合适参数的习惯。

this extended comment stream