C++ Logo


Advanced search

Template specializations in modules

From: Charles Milette <charlesmilette_at_[hidden]>
Date: Tue, 1 Jun 2021 17:28:08 -0400
Hello everyone!

While trying out modules, I found a pretty glaring issue concerning
template (partial) specializations and would like this mailing list's
opinion about it before submitting a CWG Defect Report.

Consider the following module:

export module foo;

import <cstddef>;
import <string>;
import <functional>;

export struct bar
    std::string buz;

struct std::hash<bar>
    std::size_t operator()(const bar& value) const
        return std::hash<std::string>()(value.buz);

which is then consumed in the following manner:

import <iostream>;
import <functional>;
import foo;

int main()
    std::cout << std::hash<bar>()({"test"});

One might expect this to call the specialization that was declared in
foo, but it actually doesn't (at least on MSVC).
The standard is unclear about explicit or partial specializations from
modules where the primary template comes from another module. This is
where the defect comes from, it should be clearer about this behavior.

So, the primary template is picked and since std::hash's primary
template has no idea what to do with bar, it fails to compile.
A workaround is using a header unit for such specializations and
importing it where needed, but this has the issue of not being able to
reach into a module's implementation details (which could be required,
especially for more complex things like std::coroutine_traits
specializations), as well as generally being a header (my primary use
for modules is to remove use of headers so adding new headers as
workaround is counter-productive)

MSVC implements an extension where writing "export template<...>" on a
specialization (or putting the specialization in an export
namespace/scope) will implicitly export the primary template and
specializations from the current module, and multiple modules can
contribute to specializations. It makes the snippets above work as one
would expect.

I would suggest standardizing this extension, to make this behavior
1. more consistent between compilers
2. make more sense by default: non-exported specializations don't
contribute in TUs that import the module, while exported
specializations do.
3. allow the use of some core language facilities (like coroutines and
structured bindings) with user types purely through modules.

What do you think about this?

Charles Milette

Received on 2021-06-01 16:28:21