The Greedy Constructor

#include <iostream>
#include <string>
#include <utility>

class Session {
public:
    template <class T>
    explicit Session(T&& id) : id_(std::forward<T>(id)) { std::cout << "tmpl "; }

    Session(const Session& o) : id_(o.id_) { std::cout << "copy "; }

private:
    std::string id_;
};

int main() {
    const Session a{std::string("abc")};   // prints "tmpl "
    Session b{a};         // (1) compiles, prints "copy"
    const Session c{a};   // (2) compiles, prints "copy"
    Session d{b};         // (3) does NOT compile
}

Line (3) is rejected with an error about constructing std::string from Session, and the diagnostic blames an instantiation of Session::Session<Session&>. Why does it fail when (1) and (2) succeed?

A

Brace initialization always prefers a template constructor over a non-template one.

B

For a non-const lvalue source the constructor template deduces T = Session&, giving an exact-match Session& parameter that beats the copy constructor's const Session&, so the template is selected and then fails to initialize id_.

C

Declaring a constructor template implicitly deletes the copy constructor, so no copy is available for a non-const source.

D

explicit on the template makes brace initialization skip the copy constructor whenever the source is non-const.