Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Have there ever been silent behavior changes in C++ with new standard versions?

(I'm looking for an example or two to prove the point, not a list.)

Has it ever been the case that a change in the C++ standard (e.g. from 98 to 11, 11 to 14 etc.) changed the behavior of existing, well-formed, defined-behavior user code - silently? i.e. with no warning or errors when compiling with the newer standard version?

Notes:

  • I'm asking about standards-mandated behavior, not about implementer/compiler author choices.
  • The less contrived the code, the better (as an answer to this question).
  • I don't mean code with version detection such as #if __cplusplus >= 201103L.
  • Answers involving the memory model are fine.
like image 405
einpoklum Avatar asked Aug 06 '20 19:08

einpoklum


3 Answers

The return type of string::data changes from const char* to char* in C++ 17. That could certainly make a difference

void func(char* data)
{
    cout << data << " is not const\n";
}

void func(const char* data)
{
    cout << data << " is const\n";
}

int main()
{
    string s = "xyz";
    func(s.data());
}

A bit contrived but this legal program would change its output going from C++14 to C++17.

like image 82
john Avatar answered Oct 04 '22 11:10

john


The answer to this question shows how initializing a vector using a single size_type value can result in different behavior between C++03 and C++11.

std::vector<Something> s(10);

C++03 default-constructs a temporary object of the element type Something and copy-constructs each element in the vector from that temporary.

C++11 default-constructs each element in the vector.

In many (most?) cases these result in equivalent final state, but there is no reason they have to. It depends on the implementation of Something's default/copy constructors.

See this contrived example:

class Something {
private:
    static int counter;

public:
    Something() : v(counter++) {
        std::cout << "default " << v << '\n';
    }

    Something(Something const & other) : v(counter++) {
        std::cout << "copy " << other.v << " to " << v << '\n';
    }

    ~Something() {
        std::cout << "dtor " << v << '\n';
    }

private:
    int v;
};

int Something::counter = 0;

C++03 will default-construct one Something with v == 0 then copy-construct ten more from that one. At the end, the vector contains ten objects whose v values are 1 through 10, inclusive.

C++11 will default-construct each element. No copies are made. At the end, the vector contains ten objects whose v values are 0 through 9, inclusive.

like image 31
cdhowie Avatar answered Oct 04 '22 13:10

cdhowie


The standard has a list of breaking changes in Annex C [diff]. Many of these changes can lead to silent behavior change.

An example:

int f(const char*); // #1
int f(bool);        // #2

int x = f(u8"foo"); // until C++20: calls #1; since C++20: calls #2
like image 25
cpplearner Avatar answered Oct 04 '22 11:10

cpplearner