0

I have a std:atomic_bool which I have defined as this in my code:

class A
{
public:
A();
A(const A&);
~A();

std::atomic_bool isTrue;
}

A:A()
{
isTrue= false;
}

A::A(const A&) : isTrue(false)
{}

Then I do the following:

class B
{
A aObj;

public:
bool getBool()
{
return GetNumberOfUsers() > 1 ? aObj.isTrue : true;
}

I get the following error on the getBool() function.

Error C2280 'std::atomic::atomic(const std::atomic &)': attempting to reference a deleted function

I understand that this happens because the copy constructor of atomics are deleted. But I have followed this answer and have defined the copy constructor. I am at a lost why the error is still coming up. I also tried:

class A
    {
    ...  
    A(const A&) = delete;

But still got the error.

Edit: After some troubleshooting I came across a strange behavior. I found out that if I change my ternary if conditional in B::getBool() as the following, I will not get the error.

bool getBool()
{
   // return GetNumberOfUsers() > 1 ? aObj.isTrue : true;
   if (GetNumberOfUsers() > 1)
        return aObj.isTrue ;
   else
        return true;
}

Now this is even more confusing.

EDIT 2:

After a little more troubleshooting I found the issue is probably the return type. in a ternary if conditional exp1 ? exp2 : exp3, the return type is the type of exp2 (as explained here). So in this case, the return type of B::getBool() becomes atomic_bool, not atomic. When I added a static_cast the code to the following I do not get the error anymore.

bool getBool()
{
        return GetNumberOfUsers() > 1 ? static_cast<bool>(aObj.isTrue) : true;
}

However I still don't know why this would throw the copy constructor error. Thank you.

Madu
  • 33
  • 3
  • In none of the bits of code you posted is at any point a copy being made of anything whatsoever. Thus, the error you mention cannot possibly be happening within the code you posted. Please edit your answer to include the piece of code in which the error happens together with an indication where exactly (which line) the error happens. Ideally, post an [MCVE](https://stackoverflow.com/help/mcve). – Michael Kenzel Apr 08 '19 at 02:16
  • Thank you @MichaelKenzel. Yes that is what's confusing me. It does not seem to call the copy constructor at all. In fact this is a project that I am trying to migrate to VS 2017 from 2012. In VS 2012 this compiled fine without a copy constructor. I added the copy constructor assuming that is the case. Unfortunately this is part of a large production code so I am not able to provide a MCVE. Appreciate the support. – Madu Apr 08 '19 at 02:24
  • How do you know the cause of the problem to be in the bits of code up there and not somewhere in a completely different place? If the problem had to do with attempting to copy an `A`, then why does the error message not complain about the deleted copy-ctor of `A` rather than the deleted copy-ctor of `std::atomic`? I would suspect that there must be a different bit of code somewhere that actually attempts to copy an `std::atomic`, especially since you report that explicitly deleting the copy-ctor of `A` does have no effect on the error at all!? – Michael Kenzel Apr 08 '19 at 02:31
  • Thank you @MichaelKenzel. The reason I believe the error is in that piece of code is because, if I change B::getBool() {return true;}, I do not get the error anymore. Also, B::getBool() is the only place where aObj is used in class B (not sure if it helps). – Madu Apr 08 '19 at 03:36
  • And your actual `getBool()` function definitely returns a `bool` and not an `atomic_bool`!? – Michael Kenzel Apr 08 '19 at 03:39
  • @MichaelKenzel It returns a `bool`. Not an `atomic_bool`. After playing around I found that the ternary if conditional that is used is the one causing the problem. Very strange. I updated the question. Appreciate any input. – Madu Apr 08 '19 at 03:54
  • @MichaelKenzel I updated the answer and it seems that when I `static_cast` it works fine. Can you explain why you asked about the return type? How would the return type of `atomic_bool` give the copy constructor deleted error? Thank you. – Madu Apr 08 '19 at 04:22
  • See my answer below. Summary: the error was in the bit of code you didn't show… ;-) – Michael Kenzel Apr 08 '19 at 04:30
  • Thanks @MichaelKenzel. Yes. Never expected that to be the root cause :) – Madu Apr 08 '19 at 04:34
  • Yes, to be fair, it's quite non-obvious… – Michael Kenzel Apr 08 '19 at 04:37
  • Actually, I just noticed that my original answer was wrong. This conditional expression should not compile. I'll update my answer… – Michael Kenzel Apr 08 '19 at 04:55
  • Please see my updated answer below. I believe the specific error you witnessed must have been a compiler bug. The code was always ill-formed, however, just for different reasons… – Michael Kenzel Apr 08 '19 at 05:10

1 Answers1

1

You conditional expression

GetNumberOfUsers() > 1 ? aObj.isTrue : true;

should not compile. However, the reason for that has nothing to do with the copy constructor of std::atomic<bool>. The two expressions this conditional switches between are, first, an lvalue of class type std::atomic<bool> and, second, a prvalue of non class type bool. There exists an implicit conversion sequence from bool to std::atomic<bool>. There also exists an implicit conversion sequence from std::atomic<bool> to bool. Based on [expr.cond]/4, this program should, therefore, be ill-formed.

I believe the fact that MSVC apparently attempts to make this expression result in an std::atomic<bool> must be a bug in MSVC. Note that current versions of MSVC, when switched into conformance mode (via the /permissive- option; everyone should be using this if they can), will correctly diagnose the issue, in agreement with other compilers. live demo here

As you've already learned yourself, using aObj.load() to get its bool value, explicitly converting aObj.isTrue to bool, or replacing the conditional expression with an if statement will fix the issue…

Michael Kenzel
  • 15,508
  • 2
  • 30
  • 39