Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

template with lambda as unique default parameter on each instantiation

I'm looking for a way to automatically make default template parameter be unique each time a template is instantiated. Since unnamed function objects created by lambda expressions have different types I thought of adopting them somehow. With recent changes to standard daft removing "A lambda-expression shall not appear in ... a template-argument" restriction (see Wording for lambdas in unevaluated contexts) it seemed like a good idea. So I wrote the following kinda working snippet that compiles on recent gcc and clang:

#include <type_traits>

template<void ( * ) (void) = [](){}> class
unique final {};

static_assert(false == ::std::is_same_v<unique<>, unique<>>);

int main()
{
    return 0;
}

Is this a viable approach or one of those "ill-formed, no diagnostic is required" cases?

Some additional context: I want to use this to implement Ada-style strong type definitions that should work in a single translation unit without manually inventing unique tags that would be otherwise unused:

struct _tag_WowInt {};
using Int = type<int, _tag_WowInt>;
struct _tag_SoUnique {};
using DifferentInt = type<int, _tag_SoUnique>;

Upd1: I would like to mention that approaches involving __COUNTER__ or similar macros won't work in general case because they will be expanded by preprocessor only once and won't yield unique types when used inside of template for example.

like image 594
user7860670 Avatar asked Jan 28 '19 21:01

user7860670


1 Answers

I believe that you are right, it seems to me that is "ill-formed, no diagnostic required". I think this is covered by [temp.res/8.4] and [temp.res/8.5]:

(8.4) ― a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, or

(8.5) ― the interpretation of such a construct in the hypothetical instantiation is different from the interpretation of the corresponding construct in any actual instantiation of the template. [Note: This can happen in situations including the following:

(8.5.1) ― a type used in a non-dependent name is incomplete at the point at which a template is defined but is complete at the point at which an instantiation is performed, or

(8.5.2) ― lookup for a name in the template definition found a using-declaration, but the lookup in the corresponding scope in the instantiation does not find any declarations because the using-declaration was a pack expansion and the corresponding pack is empty, or

(8.5.3) ― an instantiation uses a default argument or default template argument that had not been defined at the point at which the template was defined, or

(8.5.4) ― constant expression evaluation within the template instantiation uses

(8.5.4.1) ― the value of a const object of integral or unscoped enumeration type or

(8.5.4.2) ― the value of a constexpr object or

(8.5.4.3) ― the value of a reference or

(8.5.4.4) ― the definition of a constexpr function, and that entity was not defined when the template was defined, or

(8.5.5) ― a class template specialization or variable template specialization that is specified by a non-dependent simple-template-id is used by the template, and either it is instantiated from a partial specialization that was not defined when the template was defined or it names an explicit specialization that was not declared when the template was defined. — end note]

Even though your use case is not explicitly listed in the examples of the note, in my understanding the requirement implies that unique<> must refer to the same thing throughout the whole program, otherwise it is ill-formed, no diagnostic required.

This was CWG1850. The Committee appear to dislike this kind of stateful meta-programming. The constexpr counter no longer works in newer versions of the compilers.

like image 50
metalfox Avatar answered Oct 07 '22 00:10

metalfox