Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can we not declare a deduction guide outside an inline namespace?

Example:

namespace X{
    inline namespace Y{
        template<typename T>
        struct A{
        };
    }
}
namespace X{
    template<typename Z>
    A(std::vector<Z>) -> A<Z>;
}

This causes a compile error in Clang 11, which says "Deduction guide must be declared in the same scope as template X::Y::A"

Similar to the template specialization, the deduction guide should also be declared within the same semantic scope as the class template. So why can I specialize the class template outside the inline namespace, but for a deduction guide I can't?

Especially, this causes another problem:

template<typename T>
struct Q{
    operator std::vector<T>() {
        return {};
    }
};

namespace std{
    template<typename T>
    vector(Q<T>) -> vector<T>;
}

The compiler refuses if I want to define a class template with a conversion to std::vector and declare a deduction guide for it. In this case (for libc++), I have to declare it in namespace std::__1.

Is there some solution or explanation in the CPP standard?

like image 877
RedFog Avatar asked Aug 13 '20 11:08

RedFog


2 Answers

so why I can specialize the class template outside the inline namespace, but for deduction guide I can't?

Because you are allowed to specialize the template. From C++ standard [namespace.def]/7:

Members of an inline namespace can be used in most respects as though they were members of the enclosing namespace. Specifically, the inline namespace and its enclosing namespace are both added to the set of associated namespaces used in argument-dependent lookup whenever one of them is, and a using-directive that names the inline namespace is implicitly inserted into the enclosing namespace as for an unnamed namespace. Furthermore, each member of the inline namespace can subsequently be partially specialized, explicitly instantiated, or explicitly specialized as though it were a member of the enclosing namespace

For the deduction guide it needs to be in the same scope as the class template. From the standard [temp.deduct.guide]/3:

[...] A deduction-guide shall be declared in the same scope as the corresponding class template and, for a member class template, with the same access. [...]

Solution would be to explicitly give X::Y scope:

namespace X::inline Y{
    template<typename Z>
    A(std::vector<Z>) -> A<Z>;
}
like image 58
Waqar Avatar answered Oct 23 '22 14:10

Waqar


The intent behind template specialization is that you can add specializations to a template even if you're not the author of the template. One might do this because they are the author of a type which is being used by this specialization. The C++ standard library's rules forbid adding declarations to the std namespace except for template specializations for precisely this reason.

Deduction guides are not like template specializations. They are considered part of the class template's definition, much like constructors and other member functions. As such, they are expected to be written by the creator of the class, typically immediately following the template class's definition. Given these expectations, it doesn't make sense for deduction guides to exist in a scope other than the scope of the template class definition itself.

Basically, you are not meant to be able to add deduction guides to someone else's class templates.


The very first version of the CTAD proposal, as well as every derivative version thereof, focuses on mapping constructor arguments to class template parameters. What will eventually be known as "deduction guides" were first discussed as "Canonical factory functions". But the text around it is particularly telling:

We suggest a notation to allow constructors to specify their template parameters by either explicitly declaring the signatures for any further needed constructor deductions outside the class

Notice how focused the text is on "constructors". These canonical factory functions are maps between constructors and template arguments. They are considered, conceptually at least, to be constructors of a sort. After all, implicit guides are generated from constructors, so it stands to reason that explicit guides are conceptually equivalent to a class constructor.

Indeed, the prototypical example of why you need explicit deduction guides (that is, why you can't rely entirely on implicit guides) is focused on the constructors of the type. Namely, vector's iterator constructor:

template<typename Iter>
vector(Iter first, Iter last);

A deduction guide is needed to access this constructor because Iter doesn't obviously map to the template parameters of vector<T, A>.

The bottom line is this: explicit deduction guides are built around a class's constructors (though those constructors don't have to exist). They exist to map constructor argument types to class template parameters. If you cannot add a constructor to a class from outside of the class's definition, then it stands to reason that you cannot add an explicit deduction guide from outside of the class's definition either.

Obviously explicit guides are written outside of a template class's definition, but the principle is the same: guides are part of a class's interface.

Implicit conversion via operator Typename does not add a constructor to Typename. It may permit Typename(other_type) to work, but as far as the language standard is concerned, this is a copy/move into Typename. It isn't modifying the definition of Typename.

like image 44
Nicol Bolas Avatar answered Oct 23 '22 12:10

Nicol Bolas