5

The following code:

#include <iostream>
#include <iomanip>
#include <string>
#include <utility>
using namespace std;

struct Foo
{
    std::string s;
    int i;
};

int main()
{
    cout << boolalpha << is_nothrow_constructible<Foo>::value << endl;
    cout << is_nothrow_constructible<pair<string, int>>::value << endl;

    cout << is_nothrow_move_constructible<Foo>::value << endl;
    cout << is_nothrow_move_constructible<pair<string, int>>::value << endl;

    return 0;
}

produces the following output when compiled with g++ -std=c++11:

true
false
true
true

Why is std::pair<string, int> not nothrow constructible, while Foo is, and why it is nothrow move constructible?

Nick
  • 10,309
  • 21
  • 97
  • 201
  • 1
    Do you want a reason the commite did what it did or are you looking for an answer like: because the standard says it isn't? If you look [here](http://en.cppreference.com/w/cpp/utility/pair/pair) you will see non of the constructors are `noexcept` – NathanOliver Jun 11 '18 at 21:06
  • 1
    Theory: `Foo`'s compiler-provided constructors automatically propagate the nothrow-ness of the constructors they call. `std::pair`'s constructors are explicitly declared and the standard doesn't require them to propagate nothrow-ness. I don't think there's a reason they couldn't be made to, they just don't. – cdhowie Jun 11 '18 at 21:07
  • @NathanOliver I would like to know what is the reason behind it, also, even if not stated on your link, the move ctor is apparently nothrow constructible. – Nick Jun 11 '18 at 21:08
  • @cdhowie `std::pair::pair` _"...Does not throw exceptions unless one of the specified operations (e.g. constructor of an element) throws...._ http://en.cppreference.com/w/cpp/utility/pair/pair – Richard Critten Jun 11 '18 at 21:09
  • @Nick I'm going to go out on a limb and say that is your compiler being nice to you. `std::vector` requires the move constructor to be `noexcept` for it to move elements when resizing and your implmentation knows that and marked it as such so you can get the performance gain. Most likely this is defect in the standard since we have conditional `noexcept` specifers. – NathanOliver Jun 11 '18 at 21:14
  • It is a pain to make everything conditionally noexcept, especially without `noexcept(auto)`, so the standard only specifies it in rare cases, and implementations only add it where the benefit is clear. – Marc Glisse Jun 11 '18 at 21:26
  • @RichardCritten "Does not throw exceptions" is a very different thing from "is declared not to throw exceptions." – cdhowie Jun 11 '18 at 21:31
  • A similar question with interesting answers [Why doesn't N3421 provide the noexcept specifier?](https://stackoverflow.com/questions/21752446/why-doesnt-n3421-provide-the-noexcept-qualifier) – Bo Persson Jun 11 '18 at 21:39

2 Answers2

3

Interestingly enough, none of the constructors is declared noexcept under any conditions. Likely a typical Standard defect (only the textual description below promises to not throw any exceptions if none of the elements does.)

bipll
  • 11,747
  • 1
  • 18
  • 32
0

Usually, defaulted constructors are defined as noexcept whenever possible. However, std::pair has default constructor defined without noexcept (i.e. not nothrow). You can check it yourself here.

You can see the default constructor for std::pair is defined without noexcept (first item from the link), and move constructor for std::pair is defaulted (8th item from the link).

Since you have not declared/defined any constructor for Foo, its constructors are defaults, therefore noexcept.

ccld44
  • 109
  • 7
  • 1
    The really doesn't answer what the OP wants to know. Yes it shows that it is not defined to be so but why was that decision made, and why does their implementation say it is nothrow move constructable? – NathanOliver Jun 11 '18 at 21:22
  • @NathanOliver It is because the member types can have default constructor that can throw, and we cannot have both nothrow and throw version of default constructor of `std::pair`. The move constructor is defaulted and follows the standard. – ccld44 Jun 11 '18 at 21:29
  • But we can have conditional `noexcept` specifiers so you can specify it to be `noexcept` when the underlying types are `noexcept`. – NathanOliver Jun 11 '18 at 21:31
  • @ccld44 *"we cannot have both nothrow and throw version of default constructor"* Not on the same type, but we can define a template type for which an instantiation's constructor can be nothrow or throw depending on the type parameters. Such a thing is possible here. – cdhowie Jun 11 '18 at 21:33
  • @NathanOliver You are right. It is possible that conditional `noexcept` for the default constructor can solve the issue. I see that `std::vector` is doing what you described. – ccld44 Jun 11 '18 at 21:38