I'm reading the source code of the main json11 header file.
It contains the following declaration:
template <class T, class = decltype(&T::to_json)>
Json(const T & t) : Json(t.to_json()) {}
I'm trying to find some documentation about this usage of decltype and class inside a template declaration but no success.
Does this construction/usage has a name in C++? Any good reference about it?
It's using SFINAE ("Substitution Failure Is Not An Error"), a common technique for advanced template stuff. In this case, it's used as a crude(1) test whether the type T has a function named to_json.
How it works: if the expression T::to_json is well-formed (there is something named to_json inside the type T), decltype(T::to_json) denotes a valid type and the constructor template can be used normally.
However, if T::to_json is ill-formed (i.e. if there is no to_json member inside T), it means substituting the template argument for T has failed. Per SFINAE, this is not an error of the entire program; it just means that the template is removed from further consideration (as if it was never part of the class).
The effect is thus that if type T has a member to_json, you can use an object of type T to initialise a Json object. If there is no such member in T, the constructor will not exist.
(1) I'm saying crude test because this only checks that T has such a member. It doesn't check that the member is a function which can be invoked without arguments and returns something another constructor of Json can accept. A tighter-fitting test might look something like this:
template <class T, class = std::enable_if_t<std::is_constructible<Json, decltype(std::declval<const T>().to_json())>::value>>
Json(const T & t) : Json(t.to_json()) {}
[Live example]
Related
Given the following code to parse:
template <typename T, int N=3>
class Base {};
using BaseFloat1 = Base<float, 1>;
I can get the TypeAliasDecl from the last line, get the CXType from that, and the use clang_Type_getNumTemplateArguments() to see that there are two template arguments there, but the only option for retrieving the arguments is clang_Type_getTemplateArgumentAsType(), which obviously fails for the second, IntegerLiteral argument.
Is there any way to get the non-type template argument directly, other than inspecting the children of the TypeAliasDecl cursor?
I have a class MyClass<size_t, type>.
I would like to add conditional template instantiation using std::is_same and std::conditional.
However, both types for the first parameter are the same and are size_t.
As expected when MYSIZE1 and MYSIZE2 are the same, class<MYSIZE,int> will give an error.
How can I perform this conditional compilation considering that the only change is in the value of the template parameter?
template class MyClass<
std::conditional< (!std::is_same<MyClass<MYSIZE1,int>,
MyClass<MYSIZE2,int>
>::value),
MyClass<DUMMYSIZE,int>,
Myclass<MYSIZE1, int>
>::type,
int>;
This does not work obviously as conditional returns MyClass type.
Consider following
struct dummy{};
dummy d1;
dummy d2;
template<dummy* dum>
void foo()
{
if (dum == &d1)
; // do something
else if (dum == &d2)
; // do something else
}
Now, it is possible to call foo like this
foo<&d1>();
foo<&d2>();
and everything works as expected. But following does not
constexpr dummy* dum_ptr = &d1;
foo<dum_ptr>();
With this error from Visual studio
error C2975: dum_ptr: invalid template argument for foo, expected compile-time constant expression
While this works
constexpr dummy& dum_ref = d1;
foo<&dum_ptr>();
In visual studio, but not in G++, because of
note: template argument deduction/substitution failed:
error: & dum_ref is not a valid template argument for dummy* because it is not the address of a variable
foo<&dum_ref>();
EDIT:
Since C++17, std::addressof is being marked as constexpr, so I would guess it should work.
GCC is right on this one.
The expressions are definitely constant-expressions*, since they are assigned to a constexpr variable. However, until c++14, there are additional restrictions on what is allowed for a pointer template argument.
C++14 draft N4140 [temp.arg.nontype]
1 A template-argument for a non-type, non-template template-parameter shall be one of:
for a non-type template-parameter of integral or enumeration type, a converted constant expression (5.19) of the type of the
template-parameter; or
the name of a non-type template-parameter; or
a constant expression (5.19) that designates the address of a complete object with static storage duration and external or internal
linkage or a function with external or internal linkage, including
function templates and function template-ids but excluding non-static
class members, expressed (ignoring parentheses) as
&id-expression, where the id-expression is the name of an object or
function, except that the & may be omitted if the name refers to a
function or array and shall be omitted if the corresponding
template-parameter is a reference; or
a constant expression that evaluates to a null pointer value (4.10); or
a constant expression that evaluates to a null member pointer value (4.11); or
a pointer to member expressed as described in 5.3.1; or
a constant expression of type std::nullptr_t.
For foo<dum_ptr>(), dum_ptr isn't expressed as &name, and for foo<&dum_ref>(), dum_ref isn't the name of the object, it's the name of a reference to the object, so both are disallowed as template arguments.
These restrictions are lifted in c++17 to allow any constexpr, so thats why it works there:
C++17 draft N4606 - 14.3.2 Template non-type arguments [temp.arg.nontype]
1 A template-argument for a non-type template-parameter shall be a
converted constant expression (5.20) of the type of the
template-parameter. For a non-type template-parameter of reference or
pointer type, the value of the constant expression shall not refer to
(or for a pointer type, shall not be the address of):
(1.1) a subobject (1.8),
(1.2) a temporary object (12.2),
(1.3) a string literal (2.13.5),
(1.4) the result of a typeid expression (5.2.8), or
(1.5) a predefined __func__ variable (8.4.1).
As usual, clang gives the best error messages:
https://godbolt.org/g/j0Q2bV
*(see Address constant expression and Reference constant expression)
I've seen many examples of using a lambda expression as a template argument, but when i was reading the reference page for lambda functions at cppreference.com,
it has this short sentence:
Lambda-expressions are not allowed in unevaluated expressions, template arguments, alias declarations, typedef declarations, and anywhere in a function (or function template) declaration except the function body and the function's default arguments.
I was very confused, is this sentence wrong or I did not understand it correctly?
What you can do:
template <class>
struct Foo;
auto l = []{};
Foo<decltype(l)> f;
What you cannot do:
template <SomeType lambda>
struct Foo;
Foo<[]{}> f;
In other words, the type of the lambda is a normal type like any other, but the lambda-expression itself can't be used to specialize a template. The same applies to unevaluated contexts such as the operands of decltype and sizeof.
The code below contains a reference to Enum::name (notice no type parameter).
public static <T extends Enum<T>> ColumnType<T, String> enumColumn(Class<T> klazz) {
return simpleColumn((row, label) -> valueOf(klazz, row.getString(label)), Enum::name);
}
public static <T, R> ColumnType<T, R> simpleColumn(BiFunction<JsonObject, String, T> readFromJson,
Function<T, R> writeToDb) {
// ...
}
Javac reports a warning during compilation:
[WARNING] found raw type: java.lang.Enum missing type arguments for
generic class java.lang.Enum
Changing the expression to Enum<T>::name causes the warning to go away.
However Idea flags the Enum<T>::name version with a warning that:
Explicit type arguments can be inferred
In turn Eclipse (ECJ) doesn't report any problems with either formulation.
Which of the three approaches is correct?
On one hand raw types are rather nasty. If you try to put some other type argument e.g. Enum<Clause>::name will cause the compilation to fails so it's some extra protection.
On the other hand the above reference is equivalent to e -> e.name() lambda, and this formulation doesn't require type arguments.
Enviorment:
Java 8u91
IDEA 15.0.3 Community
ECJ 4.5.2
There is no such thing as a “raw method reference”. Whilst raw types exist to help the migration of pre-Generics code, there can’t be any pre-Generics usage of method references, hence there is no “compatibility mode” and type inference is the norm. The Java Language Specification §15.13. Method Reference Expressions states:
If a method or constructor is generic, the appropriate type arguments may either be inferred or provided explicitly. Similarly, the type arguments of a generic type mentioned by the method reference expression may be provided explicitly or inferred.
Method reference expressions are always poly expressions
So while you may call the type before the :: a “raw type” when it referes to a generic class without specifying type arguments, the compiler will still infer the generic type signature according to the target function type. That’s why producing a warning about “raw type usage” makes no sense here.
Note that, e.g.
BiFunction<List<String>,Integer,String> f1 = List::get;
Function<Enum<Thread.State>,String> f2 = Enum::name;
can be compiled with javac without any warning (the specification names similar examples where the type should get inferred), whereas
Function<Thread.State,String> f3 = Enum::name;
generates a warning. The specification says about this case:
In the second search, if P1, ..., Pn is not empty and P1 is a subtype of ReferenceType, then the method reference expression is treated as if it were a method invocation expression with argument expressions of types P2, ..., Pn. If ReferenceType is a raw type, and there exists a parameterization of this type, G<...>, that is a supertype of P1, the type to search is the result of capture conversion (§5.1.10) applied to G<...>;…
So in the above example, the compiler should infer Enum<Thread.State> as the parametrization of Enum that is a supertype of Thread.State to search for an appropriate method and come to the same result as for the f2 example. It somehow does work, though it generates the nonsensical raw type warning.
Since apparently, javac only generates this warning when it has to search for an appropriate supertype, there is a simple solution for your case. Just use the exact type to search:
public static <T extends Enum<T>> ColumnType<T, String> enumColumn(Class<T> klazz) {
return simpleColumn((row, label) -> valueOf(klazz, row.getString(label)), T::name);
}
This compiles without any warning.