2

Let's assume there is a function f which receives two variables one by reference and another is a constant reference.

If I pass a single variable as both arguments an error occurs:

result:

a: 6
b: 6

code

void f(int &a,const int &b)
{
    a=a+1;
    std::cout<<"a: "<<a<<"\n";
    std::cout<<"b: "<<b<<"\n";
}

int main()
{
    int m=5;
    f(m,m);
    return 0;
}

Does this problem have any known name? Also, is there any way to warn compiler to be careful about that?

SLePort
  • 15,211
  • 3
  • 34
  • 44
ar2015
  • 5,558
  • 8
  • 53
  • 110
  • 2
    The term you are looking for is ["aliasing"](https://en.wikipedia.org/wiki/Aliasing_(computing)). I'm not sure what you mean by "warn the compiler" - the compiler is perfectly aware of the issue. After all, it generated code that produced correct output, didn't it? – Igor Tandetnik Apr 16 '16 at 03:27
  • 2
    Why is this a problem? You passed in one reference that can be modified and one reference that can't. They still reference the same data you just can't change the data through b. You didn't make the data const just the reference to the data. – Pemdas Apr 16 '16 at 03:30
  • @Pemdas, this code is error prone. – ar2015 Apr 16 '16 at 03:34
  • 1
    the programmer is error prone..lol – Pemdas Apr 16 '16 at 03:35
  • @Pemdas think about `A` and `B` as a matrix and `f` as a matrix operation. if user gives the same matrix to them the function fails. for example `inv(A,A)` . how to fixing that? – ar2015 Apr 16 '16 at 03:39
  • if you don't want the data modified then don't pass it by reference or copy it before you used it. – Pemdas Apr 16 '16 at 03:41
  • @Pemdas, i do not do it on purpose. it might happen by a mistake. – ar2015 Apr 16 '16 at 04:08

2 Answers2

5

This code is well-defined and there is no problem.

The code const int &b means that:

  • b refers to an int
  • The expression b may not be used to modify that int.

However it is perfectly fine for the int to be modified by other means. For example, via a here. When compiling f, the compiler must take into account that a and b might both refer to the same object.


Some compilers have extensions to specify that function parameters (or other variables) should not alias; e.g. in MSVC++ 2015 you can write:

void f(int & __restrict a, const int & __restrict b)

and then the compiler may compile f assuming that &a != &b, i.e. it might output 6 5 for your code, which would now be silent undefined behaviour -- the compiler isn't required to diagnose violations of __restrict


When writing a function that takes multiple reference or pointer parameters of the same type (excluding qualifications), or char, you must be aware that it's possible some parameters will alias the other parameters. You could hand this by one of the following:

  • Write your code so that it works even if the parameters alias
  • Include a check like if ( &a == &b ) return;
  • Use __restrict and document that the onus is on the caller to not alias.

A common case this shows up is the overloaded operator= for a class. This function needs to support someone writing x = x; , i.e. *this may alias the function parameter. Sometimes people solve this by checking if ( &a == this ) return;, and sometimes they omit that check but design the implementation so that it still works even if those were equal.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • Wouldn't the first bullet point actually be: "`b` refers to a `const int`"? – RamblingMad Apr 16 '16 at 04:19
  • @CoffeeandCode No, that's the whole point. `b` refers to storage that can store an `int`. That storage may or may not be `const`. – M.M Apr 16 '16 at 04:21
  • Sorry, I mean: Wouldn't the first bullet point actually be: "b refers to a *logically* `const` `int`"? – RamblingMad Apr 16 '16 at 04:25
  • @CoffeeandCode No it wouldn't. – M.M Apr 16 '16 at 04:25
  • 1
    References can't be c.v qualified, so the c.v qualifier must refer to the type referenced. – RamblingMad Apr 16 '16 at 04:29
  • @CoffeeandCode Yes. Reference to a const type means that the reference may not be used to modify the object being referred to. It doesn't mean that the object being referred to must be a const object, nor a "logically const object" (whatever that is) – M.M Apr 16 '16 at 04:44
  • [Refer to the most up-voted answer](http://stackoverflow.com/questions/3830367/difference-between-logical-and-physical-const-ness) – RamblingMad Apr 16 '16 at 05:02
  • Not a great answer IMO (the most-upvoted one), in any case it's not really relevant to the behaviour of the code in this question – M.M Apr 16 '16 at 05:05
  • Well, I didn't want to tell you to go read Scott Meyers Effective C++ haha My point is to do with your answer not the question. "`b` refers to a `const int`" doesn't imply that the object itself is bit-wise `const` but that it must be treated like it was; hence, *"logically"* `const`. I was just being pedantic about your first bullet ,':^) – RamblingMad Apr 16 '16 at 05:16
  • "it must be treated like it was" - not really though, e.g. the code in this question demonstrates that the object can change. It seems to me that OP's surprise comes from thinking that `const int &b` means that the int referred to by b is "logically const" or whatever, so he did not expect `++a` to be able to change `b`. – M.M Apr 16 '16 at 05:26
3

This appears to be working "correctly".

You've defined the function to take two arguments. The first argument is declared to indicate that the referenced integer a may be changed in f(), but b should not be changed.

Both arguments are passed by reference and you violate the use of const because the changing a is also referenced as b.

This behavior is what I'd expect without optimization. (The integer value that is actually stored as m was incremented before the output was generated.) However, such aliasing may be treated differently depending on the compiler and options. Its behavior is likely undefined in the general case.

geipel
  • 395
  • 1
  • 3
  • 9
  • so you mean I should always accept this risk when writing all functions? – ar2015 Apr 16 '16 at 03:46
  • 1
    Yes. Anytime you pass values by reference, _aliasing_ is potentially an issue and cannot always be caught by the compiler. In this case the calling routine defines the actual storage for `m` and passed it by reference into a routine as non-const. @igor provided a link to a decent description of this problem. – geipel Apr 16 '16 at 03:56