5

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?

Boann
  • 48,794
  • 16
  • 117
  • 146
RedFog
  • 1,005
  • 4
  • 10
  • 1
    You're not allowed to declare anything new in the namespace `std`. Your program's behavior is undefined. – Miles Budnek Aug 13 '20 at 11:15
  • @MilesBudnek "_anything_" is taking it too far. Making some specializations for user defined types in `std` is ok. `hash` is one that is often put in `std` for example. – Ted Lyngmo Aug 13 '20 at 11:45
  • @MilesBudnek could you give me some information in standard that it's exactly UB if I provide new deduction guide in `std`? – RedFog Aug 13 '20 at 12:01
  • 2
    Since you cannot add declarations to std. https://en.cppreference.com/w/cpp/language/extending_std – yao99 Aug 13 '20 at 12:06
  • "_It is allowed to add template specializations for any standard library class template to the namespace std only if the declaration depends on at least one program-defined type and the specialization satisfies all requirements for the original template, except where such specializations are prohibited_" - but a deduction guide... I don't know. – Ted Lyngmo Aug 13 '20 at 12:07
  • I think the two questions should be in two separate questions. – Ted Lyngmo Aug 13 '20 at 12:14
  • okay, in this case, it seems that it's UB right now. thanks for helps. – RedFog Aug 13 '20 at 12:26
  • You may want to add the [tag:language-lawyer] tag to attract some ... language-lawyers - but I suggest splitting the question in two first. – Ted Lyngmo Aug 13 '20 at 12:27

2 Answers2

4

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>;
}
Waqar
  • 8,558
  • 4
  • 35
  • 43
  • 1
    @TedLyngmo Right. It gives a warning without that. – Waqar Aug 13 '20 at 11:37
  • okay, you means it's exactly not allowed yet in standard. so could you tell me why it's not allowed in standard? in my mind, `inline namespace` is deigned to avoid all names being in the top namespace because of template specialization, and library versioning is allowed as well. so deduction guide is in the same case, isn't it? even if not in `std`, patching a deduction guide in `XXX::_v_2020_8` instead of `XXX` is also unacceptable for me. – RedFog Aug 13 '20 at 11:55
  • > *so could you tell me why it's not allowed in standard?* I am afraid I don't have the right answer to that. But I think it is probably because the deduction guide has to participate in deduction of the template arguments of the class so it needs to be in the same semantic scope. – Waqar Aug 13 '20 at 12:11
  • template specialization is in the same case, isn't it? the requirement is to find the name which will be specialized/provided a deduction guide, which `inline namespace` doesn't interrupt. deduction guide just provides a function declaration as well. I don't think there is any difficulty to implement it. – RedFog Aug 13 '20 at 12:38
3

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.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • the implicit or explicit user-defined conversion allows me to construct an object defined by someone else, like `std::vector(Q())` in former example. but you mean a deduction guide that aims to make `std::vector(Q())` valid is only allowed by the creator, why? the creator won't know the all of what object can be converted to his object, but I know how to convert my object to his object, just like template specialization. – RedFog Aug 13 '20 at 14:00
  • 1
    @RedFog: OK, let's say that I see `std::vector(Q())` in code. Where would I look to find out what that actually *does*? That is, what type of `vector` is being instantiated? The definition of `vector` and its guides doesn't contain it. The definition of `Q` doesn't contain it. Where should I look? With implicit conversions, the locations to look are bounded: the source type and the destination type. No third-party code can interfere. – Nicol Bolas Aug 13 '20 at 14:11
  • I didn't get your point. the supporter who defined `Q` wrote deduction guide in his code. it's obviously that if you can't know the `T` in `std::vector(Q())` by the definition of `std::vector`, you should go to look the definition of `Q`, and then you will find it. just like if you use structured binding of some type `X` but no idea with the definition of `std::tuple_size` and `std::tuple_element`, you should look the definition of `X` and find the template specialization of `std::tuple_size` and `std::tuple_element`. – RedFog Aug 13 '20 at 14:47
  • @RedFog: "*the supporter who defined Q wrote deduction guide in his code.*" But the deduction guide is for `std::vector` which is *not* "his code". CTAD is effectively considered part of the interface of a class template, and you cannot externally change the interface of a type. Just like you cannot externally overload `operator[]` or similar operators of a type. And the author of `Q` can resolve this easily enough by adding a member function `to_vector` to `Q`, which will clear up the matter entirely. – Nicol Bolas Aug 13 '20 at 14:57
  • @RedFog: In any case, this argument is irrelevant. Template specialization is meant for external, 3rd party code to be able to add specializations. Deduction guides *are not*; they are part of a class's interface. You don't have to agree with deduction guides being part of a class template's interface, but that's how the language was designed, and that's how the language treats them. You can consider it a mis-feature or a mis-design, but you asked for the reason for it. Whether you agree with it or not, this is the reason. – Nicol Bolas Aug 13 '20 at 14:59
  • honestly, I can't understand why it's just a class's interface, even if I can use a deduction guide to cause an implicit conversion, which could be exactly not what the creator wants. but I know you want to tell me 'that's what standard asks for'. so the final question is, what's the proof or implication of what you said in standard? – RedFog Aug 13 '20 at 15:23
  • @RedFog: The association of a deduction guide to a class's interface is pretty obvious just by the way the feature works. But I added a more detailed analysis to the answer. "*even if I can use a deduction guide to cause an implicit conversion*" Deduction guides can't provoke conversions of any kind. They're patterns used to detect the arguments for CTAD; nothing more. Any conversions happen only once CTAD is done and the *real* constructor is called; explicit guides aren't real constructors. – Nicol Bolas Aug 13 '20 at 15:51
  • an interesting example in [cppref](https://en.cppreference.com/w/cpp/language/class_template_argument_deduction) implies deduction guide can convert `const char*` to `std::string` by pointing out the constructor of `std::string`, which is that I said it causes an implicit conversion. and it actually proves deduction guide can affect more than deduction, and it also proves deduction guide is not a mapping from constructor to template arguments, because the same constructor can be lead to different kinds of template arguments (at least `const char*` to `const char*` and `std::string`). – RedFog Aug 13 '20 at 16:06
  • as how the deduction guide is implemented, it leads a function template declaration and transmit to the constructor. according to this, I think it's incorrect to say it's only an interface without any definition. – RedFog Aug 13 '20 at 16:10
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/219754/discussion-between-nicol-bolas-and-redfog). – Nicol Bolas Aug 13 '20 at 16:14