SFINAE-friendly test for object deletability - c++

I'm interested in programmatically determining whether a delete-expression is valid for some given object. It seems like one should be able to do this using SFINAE. The code I have come up with works like I expect with Clang 6.0.1, but fails to compile with GCC 7.3.0.
Two questions: 1) is there a better way to achieve the goal, and 2) who's right?
#include <type_traits>
using namespace std;
// C++14 variant of void_t
template< typename ...T > struct void_type { typedef void type; };
template< typename ...T > using void_t = typename void_type<T...>::type;
template< typename T, typename = void_t<> > struct Deletable : false_type { };
template< typename T >
struct Deletable<T, void_t<decltype(delete declval<T>())> > : true_type { };
// Here's the SFINAE'd test ^^^^^^^^^^^^^^^^^^^
struct A {
operator signed *() const;
};
struct B {
operator signed *() const;
operator unsigned *() const;
};
int main() {
static_assert(Deletable<A>::value, "Couldn't delete A objects but "
"should be able to.");
static_assert(!Deletable<B>::value, "C objects are deletable but "
"shouldn't be because pointer-to-object is ambiguous.");
return 0;
}
My attempt at answering q.2
tl;dr: I think Clang is right. Skip to the bottom for a few other test cases.
Here's why, using N4140.
The relevant section about the delete-expression when given a class type reads [expr.delete] p.1:
[...] If of class type, the operand [of the delete-expression] is contextually implicitly converted to a pointer to object type. The delete-expression’s
result has type void.
Note that void * is not a "pointer to object". Here we find that the result of the decltype(delete <blah>) is void: it is not a dependent expression. However, I think for SFINAE we're just interested in whether or not it's well-formed.
The conversion is done according to [conv] p.5:
An expression e of class type E appearing [as a delete-expression's operand] is
said to be contextually implicitly converted to a specified type T and
is well-formed if and only if e can be implicitly converted to a type
T that is determined as follows: E is searched for conversion
functions whose return type is cv T or reference to cv T such that T
is allowed by the context. There shall be exactly one such T.
Since T needs to be a pointer-to-object, we see that class A meets the conversion criteria (and is therefore deletable) by providing a single conversion-to-object-pointer function whereas B, providing two such functions, does not. Thus, trying to delete a B object should result in an ill-formed expression. This is what the static_asserts in the above code are supposed to ensure.
Possible alternative approach: can one test an object for "convertability to some pointer to object type"?
Carrying on, here's the definition of an invalid expression for the purpose of type substitution [type.deduct] p.8:
If a substitution results in an invalid type or expression, type
deduction fails. An invalid type or expression is one that would be
ill-formed, with a diagnostic required, if written using the
substituted arguments. [...] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [ Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the “immediate context” and can result in the program being ill-formed. — end note ]
As far as I can tell, since deleting a B object is ill-formed (with diagnostic required), the decltype(delete <b>) expression should be invalid making SFINAE kick in. Deletable then ends up inheriting from false_type, and we're golden.
With Clang, that seems to be the case. With GCC, here's the result:
main.cpp: In substitution of ‘template<class T> struct Deletable<T, typename
void_type<T, decltype (delete (declval<T>)())>::type> [with T = B]’:
main.cpp:24:32: required from here
main.cpp:24:32: error: ambiguous default type conversion from ‘B’
static_assert(!Deletable<B>::value, "Shouldn't be able to delete B objects "
^~
main.cpp:24:32: note: candidate conversions include ‘B::operator int*()
const’ and ‘B::operator unsigned int*() const’
main.cpp:24:32: error: type ‘struct B’ argument given to ‘delete’, expected
pointer
My guess is that GCC does not consider the contextual implicit conversion as part of the immediate context, so the ambiguity becomes a hard error rather than just a type deduction failure. Given the apparent intent of the "immediate context" as defined in the note above, I think the conversion should be in the immediate context. Therefore, as above, I think Clang is right.
For completeness, other possible reasons an object may not be deletable are:
class C {
~C();
public:
operator C*() const;
};
struct D {
~D() = delete;
operator D*() const;
};
struct E {
operator E*() const;
private:
void operator delete(void*);
};
int main() {
static_assert(!Deletable<C>::value, "C objects are deletable but shouldn't "
"be because destructor is private.");
static_assert(!Deletable<D>::value, "D objects are deletable but shouldn't "
"be because destructor is deleted.");
static_assert(!Deletable<E>::value, "E objects are deletable but shouldn't "
"be because deallocation function is inaccessible.");
return 0;
}
Everything works fine in Clang, and, interestingly, the static_assert() for D passes for GCC, however the C and E cases fail.
main.cpp: In instantiation of ‘struct Deletable<C>’:
main.cpp:31:32: required from here
main.cpp:11:76: error: ‘C::~C()’ is private within this context
struct Deletable<T, void_t<T, decltype(delete declval<T>())> > : true_type { };
^
main.cpp:14:5: note: declared private here
~C();
^
main.cpp: In function ‘int main()’:
main.cpp:31:5: error: static assertion failed: C objects are deletable but shouldn't be because destructor is private.
static_assert(!Deletable<C>::value, "C objects are deletable but shouldn't "
^~~~~~~~~~~~~
main.cpp:31:20: error: ‘C::~C()’ is private within this context
static_assert(!Deletable<C>::value, "C objects are deletable but shouldn't "
^~~~~~~~~~~~
main.cpp:14:5: note: declared private here
~C();
^
main.cpp: In instantiation of ‘struct Deletable<E>’:
main.cpp:35:32: required from here
main.cpp:11:76: error: ‘static void E::operator delete(void*)’ is private within this context
struct Deletable<T, void_t<T, decltype(delete declval<T>())> > : true_type { };
^
main.cpp:27:10: note: declared private here
void operator delete(void*);
^~~~~~~~
main.cpp:35:5: error: static assertion failed: E objects are deletable but shouldn't be because deallocation function is inaccessible.
static_assert(!Deletable<E>::value, "E objects are deletable but shouldn't "
^~~~~~~~~~~~~
main.cpp:35:20: error: ‘static void E::operator delete(void*)’ is private within this context
static_assert(!Deletable<E>::value, "E objects are deletable but shouldn't "
^~~~~~~~~~~~
main.cpp:27:10: note: declared private here
void operator delete(void*);
^~~~~~~~

Related

Overloading static and non-static member function with constraint

Is this code valid?
template<bool b>
struct s {
void f() const {
}
static void f() requires b {
}
};
void g() {
s<true>().f();
}
clang says yes, but gcc says no
<source>: In function 'void g()':
<source>:10:20: error: call of overloaded 'f()' is ambiguous
10 | s<true>().f();
| ~~~~~~~~~~~^~
<source>:3:14: note: candidate: 'void s<b>::f() const [with bool b = true]'
3 | void f() const {
| ^
<source>:5:21: note: candidate: 'static void s<b>::f() requires b [with bool b = true]'
5 | static void f() requires b {
| ^
Compiler returned: 1
https://godbolt.org/z/f4Kb68aee
If we go through [over.match.best.general], we get:
a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then [...]
The only argument is the object argument, and we have earlier that:
If F is a static member function, ICS1(F) is defined such that ICS1(F) is neither better nor worse than ICS1(G) for any function G, and, symmetrically, ICS1(G) is neither better nor worse than ICS1(F); otherwise,
So the premise holds: all arguments for one function have a conversion sequence no worse than the conversion sequence for the other function. So we move on to our tiebreakers...
for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,
The only argument that we could have a better conversion sequence for is the object argument, and as established, that one is equivalent. So this tiebreaker does not apply.
the context is an initialization by user-defined conversion (see [dcl.init], [over.match.conv], and [over.match.ref]) and [...]
Nope.
the context is an initialization by conversion function for direct reference binding of a reference to function type, [...]
Nope.
F1 is not a function template specialization and F2 is a function template specialization, or, if not that,
Nope.
F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in [temp.func.order], or, if not that,
Nope.
F1 and F2 are non-template functions with the same parameter-type-lists, and F1 is more constrained than F2 according to the partial ordering of constraints described in [temp.constr.order], or if not that,
Aha! In this example, we have non-template functions with the same parameter-type-lists (both are just empty). The static member function is constrained and the non-static member function is not constrained, which is the most trivial kind of "more constrained" (see [temp.constr.order]).
As such, I think that clang (and msvc) are correct to accept the program and gcc is incorrect to reject it. (submitted 103783).
Your code is ill-formed according to C++20 standard class.static.mfct#2:
There shall not be a static and a non-static member function with the same name and the same parameter types ([over.load]).
There is no exception here for the presence of requires-clause to differentiate member functions, only same name and the same parameter types. And it is exactly our case: the same name is f, and the same parameter types is empty set.
So Clang and MSVC are wrong in accepting the code. But the diagnostics of GCC is definitely confusing.
With some minor tweaks in the code (removed const in not-static member function and get its address in the code), Clang and MSVC also show to have big problems with it:
template<bool b>
struct s {
void f() {}
static void f() requires b {}
};
int main() {
s<true>().f();
void (s<true>::*x)() = &s<true>::f;
}
Demo: https://gcc.godbolt.org/z/vdq9j63Gs

implicit reinterpret cast on reference without warning/error

Just found the reason for an insidious crash to be a unchecked wild cast by the compiler, disregarding the types. Is this intended behaviour or a compiler bug?
Problem: When a type definition is involved, it is possible to make an implicit reinterpret cast, undermining the type system.
#include <iostream>
template<class A, class B>
inline bool
isSameObject (A const& a, B const& b)
{
return static_cast<const void*> (&a)
== static_cast<const void*> (&b);
}
class Wau
{
int i = -1;
};
class Miau
{
public:
uint u = 1;
};
int
main (int, char**)
{
Wau wau;
using ID = Miau &;
ID wuff = ID(wau); // <<---disaster
std::cout << "Miau=" << wuff.u
<< " ref to same object: " <<std::boolalpha<< isSameObject (wau, wuff)
<< std::endl;
return 0;
}
I was shocked to find out that gcc-4.9, gcc-6.3 and clang-3.8 accept this code without any error and produce the following output:
Miau=4294967295 ref to same object: true
Please note I use the type constructor syntax ID(wau). I would expect such behaviour on a C-style cast, i.e. (ID)wau. Only when using the new-style curly braces syntax ID{wau} we get the expected error...
~$ g++ -std=c++11 -o aua woot.cpp
woot.cpp: In function ‘int main(int, char**)’:
woot.cpp:31:21: error: no matching function for call to ‘Miau::Miau(<brace-enclosed initializer list>)’
ID wuff = ID{wau};
^
woot.cpp:10:7: note: candidate: constexpr Miau::Miau()
class Miau
^~~~
woot.cpp:10:7: note: candidate expects 0 arguments, 1 provided
woot.cpp:10:7: note: candidate: constexpr Miau::Miau(const Miau&)
woot.cpp:10:7: note: no known conversion for argument 1 from ‘Wau’ to ‘const Miau&’
woot.cpp:10:7: note: candidate: constexpr Miau::Miau(Miau&&)
woot.cpp:10:7: note: no known conversion for argument 1 from ‘Wau’ to ‘Miau&&’
Unfortunately, the curly-braces syntax is frequently a no-go in template heavy code, due to the std::initializer_list fiasco. So for me this is a serious concern, since the protection by the type system effectively breaks down here.
Can someone explain the reasoning behind this behaviour?
Is it some kind of backwards compatibility (again, sigh)?
To go full language-lawyer, T(expression) is a conversion of the result of expression to T1. This conversion has as effect to call the class' constructor2. This is why we tend to call a non-explicit constructor taking exactly one argument a conversion constructor.
using ID = Miau &;
ID wuff = ID(wau);
This is then equivalent to a cast expression to ID. Since ID is not a class type, a C-style cast occurs.
Can someone explain the reasoning behind this behaviour?
I really can't tell why is was ever part of C++. This is not needed. And it is harmful.
Is it some kind of backwards compatibility (again, sigh)?
Not necessarily, with C++11 to C++20 we've seen breaking changes. This could be removed some day, but I doubt it would.
1)
[expr.type.conv]
A simple-type-specifier or typename-specifier followed by a parenthesized optional expression-list or by a braced-init-list (the initializer) constructs a value of the specified type given the initializer. [...]
If the initializer is a parenthesized single expression, the type conversion expression is equivalent to the corresponding cast expression. [...]
2) (when T is of class type and such a constructor exists)
[class.ctor]/2
A constructor is used to initialize objects of its class type. Because constructors do not have names, they are never found during name lookup; however an explicit type conversion using the functional notation ([expr.type.conv]) will cause a constructor to be called to initialize an object.
it is possible to make an implicit reinterpret cast, undermining the type system.
ID wuff = ID(wau);
That's not an "implicit" reinterpret cast. That is an explicit type conversion. Although, the fact that the conversion does reinterpretation is indeed not easy to see. Specifically, the syntax of the cast is called "functional style".
If you're unsure what type of cast an explicit type conversion (whether using the functional syntax, or the C style syntax) performs, then you should refrain from using it. Many would argue that explicit type conversions should never be used.
If you had used static_cast instead, you would have stayed within the protection of the type system:
ID wuff = static_cast<ID>(wau);
error: non-const lvalue reference to type 'Miau' cannot bind to a value of unrelated type 'Wau'
It's often also safe to simply rely on implicit conversions:
ID wuff = wau;
error: non-const lvalue reference to type 'Miau' cannot bind to a value of unrelated type 'Wau'
Is this intended behaviour
Yes.
or a compiler bug?
No.

Why can't one compare a function pointer to a template function without explicit & on function name?

Consider the following code:
void func(int) {}
template<typename T> void templatedFunc(T) {}
int main()
{
void (*p)(int) = func;
bool test1 = p==func;
//bool test2 = p==templatedFunc<int>; // compilation error
bool test3 = p==&templatedFunc<int>; // but this works
}
If you uncomment the test2 line and try to compile the code with g++, you'll get the following error:
test.cpp: In function ‘int main()’:
test.cpp:8:21: error: assuming cast to type ‘void (*)(int)’ from overloaded function [-fpermissive]
bool test2 = p==templatedFunc<int>; // compilation error
^~~~~~~~~~~~~~~~~~
I get this result on g++ 5.3.0 and 6.2.0. At the same time, compilation with clang++ 3.6.0 succeeds without warnings.
Which compiler is correct according to the Standard here — g++, which gives an error or clang++, which doesn't?
And if g++ is right, then why is there such an asymmetry with normal functions vs templated functions regarding the need of explicit address-of operator?
This is a gcc bug, and you are in a corner case, in the C++ standard, Address of overloaded function §13.4 ([over.over]/1):
A use of an overloaded function name without arguments is resolved in certain contexts to a function, a
pointer to function or a pointer to member function for a specific function from the overload set. A function
template name is considered to name a set of overloaded functions in such contexts. The function selected
is the one whose type is identical to the function type of the target type required in the context. [ Note:
That is, the class of which the function is a member is ignored when matching a pointer-to-member-function
type. — end note ] The target can be:
(1.1) — an object or reference being initialized (8.5, 8.5.3, 8.5.4),
(1.2) — the left side of an assignment (5.18),
(1.3) — a parameter of a function (5.2.2),
(1.4) — a parameter of a user-defined operator (13.5),
(1.5) — the return value of a function, operator function, or conversion (6.6.3),
(1.6) — an explicit type conversion (5.2.3, 5.2.9, 5.4), or
(1.7) — a non-type template-parameter (14.3.2).
The overloaded function name can be preceded by the & operator. An overloaded function name shall not
be used without arguments in contexts other than those listed. [ Note: Any redundant set of parentheses
surrounding the overloaded function name is ignored (5.1). — end note ]
Do you see what is lacking in the list from (1.1) to (1.7)... built-in operators!
If you declare an overload of operator == both gcc will not complain with the comparison, more than that you do not have to explicitly specialize the template function:
void func(int) {}
template<class T>
void templatedFunc(T) {}
struct s{};
bool operator==(s, void(*)(int)){return false;}
int main()
{
void (*p)(int) = templatedFunc;
bool test1 = p==func;
bool test2 = s{} == templatedFunc<int>; // no error - no overload resolution
bool test3 = s{} == templatedFunc; // no error - overload resolution
bool test4 = p == templatedFunc<int>; // gcc error, but not an error -
// no overload resolution
//bool test5 = p == templatedFunc; // error - overload resolution not
// performed for built-int operators
}
test2 and test3 compiles with gcc. test4 does not compile on gcc, but there are no overload resolution, you explicitly specialized the function. It really should compile. test5 does not compile as stated in the standard. In this case gcc produces the exact same error message as for test4. This is surely a gcc bug.

Should a template be instantiated in short-circuit evaluation? [duplicate]

Program A produces a compilation error (as expected) since isFinite is called with a non-integral type.
Program A
#include <iostream>
class Foo {};
template<typename T>
bool isFinite(const T& t)
{
static_assert(std::is_integral<T>::value, "Called isFinite with a non-integral type");
return false;
}
int main()
{
Foo f;
std::cout << "Foo is finite? " << ((isFinite(f)) ? "yes" : "no") << "\n";
return 0;
}
However, a slight modification (see Program B) allows the program to compile (Visual Studio 2013) and produce the following output.
Program B Visual Studio 2013 Ouput
Foo is finite? yes
Program B
#include <iostream>
class Foo {};
template<typename T>
bool isFinite(const T& t)
{
static_assert(std::is_integral<T>::value, "Called isFinite with a non-integral type");
return false;
}
int main()
{
Foo f;
std::cout << "Foo is finite? " << ((true || isFinite(f)) ? "yes" : "no") << "\n";
return 0;
}
It appears that Program B is short circuiting on the logical OR operation and not attempting to compile the rest of the expression. However, this application does not compile using g++ 4.8.3 (g++ -std=c++11 -o main main.cpp). I get the following output.
main.cpp: In instantiation of 'bool isFinite(const T&) [with T = Foo]':
main.cpp:15:56: required from here
main.cpp:8:2: error: static assertion failed: Called isFinite with a non-integral type
static_assert(std::is_integral<T>::value, "Called isFinite with a non-integral type");
^
My intuition leads me to believe that the compilation failure is the correct behavior but it is curious that Visual Studio 2013 compiles successfully. My intuition is based on the fact that it is expected that the following code cannot be compiled.
#include <iostream>
struct Foo
{
void doOperation1() {}
void doOperation2() {}
};
struct Bar
{
void doOperationA() {}
void doOperation2() {}
};
template<typename T>
void performOperation(T& t, bool value)
{
if (value)
{
t.doOperation1();
}
else
{
t.doOperation2();
}
}
int main()
{
Foo f;
performOperation(f, true);
performOperation(f, false);
Bar b;
performOperation(b, false); // Fails to compile (as expected)
return 0;
}
Restated Question
Are the logical operators supposed to adhere to short circuit evaluation rules at compile time (i.e., what is the expected compilation behavior of Program B)?
Short circuit is not supposed to compile true || (whatever_ill_formed). isFinite<Foo> is instantiated as part of expression and during instantiation it should be compiled and during compilation it should static assert. After that the compiler may never evaluate isFinite<Foo>(f) because of short circuit but static assert is not supposed to happen during it.
It is unclear why Visual Studio 2013 compiles Program B. Standard only allows bypassing syntax checking of templates when template is never instantiated. Even then the code is still ill formed only diagnostics are not required. Behind the defect is perhaps the same internal issue in Visual C++ that does not let Microsoft to implement constexpr.
Edit I add some language lawyer texts from standard per #zneak request
3.2/3
A function whose name appears as a potentially-evaluated expression is
odr-used if it is the unique lookup result or the selected member of a
set of overloaded functions (3.4, 13.3, 13.4), unless it is a pure
virtual function and its name is not explicitly qualified. [Note: This
covers calls to named functions (5.2.2), operator overloading (Clause
13), user-defined conversions (12.3.2), allocation function for
placement new (5.3.4), as well as non-default initialization (8.5). A
constructor selected to copy or move an object of class type is
odr-used even if the call is actually elided by the implementation
(12.8). —end note]
5.13/1
The || operator groups left-to-right. The operands are both
contextually converted to bool (Clause 4). It returns true if either
of its operands is true, and false otherwise. Unlike |, || guarantees
left-to-right evaluation; moreover, the second operand is not
evaluated if the first operand evaluates to true.
7.1/4
In a static_assert-declaration the constant-expression shall be a
constant expression (5.19) that can be contextually converted to bool
(Clause 4). If the value of the expression when so converted is true,
the declaration has no effect. Otherwise, the program is ill-formed,
and the resulting diagnostic message (1.4) shall include the text of
the string-literal, except that characters not in the basic source
character set (2.3) are not required to appear in the diagnostic
message.
14.7.1/3
Unless a function template specialization has been explicitly
instantiated or explicitly specialized, the function template
specialization is implicitly instantiated when the specialization is
referenced in a context that requires a function definition to exist.

Variadic function pointer conversion

I am writing a library with many function objects whose classes have several operator() overloads that do not depend on the state of the classes and do not alter it. Now, I tried to make my code work with many old-style APIs (it is not a random need, I actually had to deal with such APIs) and therefore decided to make the function objects convertible to any function pointer corresponding to one of the overloads. At some point, I realized that I had too many such conversions to function pointer operators and that I should theorically be able to write a single variadic conversion operator. Here is a class implementing such a variadic operator:
struct foobar
{
template<typename... Args>
using fptr_t = void(*)(Args... args);
template<typename... Args>
operator fptr_t<Args...>() const
{
return [](Args... args) {
// Whatever
};
}
};
As you can see, I used the lambda conversion to function pointer to implement the conversion operator, which is not a problem since every function object I have is stateless. The goal was to be able to use the class as follows:
int main()
{
void(*foo)(int) = foobar();
void(*bar)(float, double) = foobar();
}
g++ has no problem compiling this code with the expected semantics. However, clang++ rejects it with a template substitution failure error:
main.cpp:21:11: error: no viable conversion from 'foobar' to 'void (*)(int)'
void(*foo)(int) = foobar();
^ ~~~~~~~~
main.cpp:11:5: note: candidate function [with Args = int]
operator fptr_t<Args...>() const
^
1 error generated.
Note that clang++ has no problem with such conversion operators as long as no variadic templates are involved. If I use a single template parameter, it will have no problem compiling the code. Now, should the code above be accepted or rejected by the compiler?
A lambda can only be converted to a function pointer if it does not capture, so your code should work. This is justified in the standard 5.1.2/p6 Lambda expressions [expr.prim.lambda] (Emphasis Mine):
The closure type for a non-generic lambda-expression with no
lambda-capture has a public non-virtual non-explicit const conversion
function to pointer to function with C++ language linkage (7.5) having
the same parameter and return types as the closure type’s function
call operator. The value returned by this conversion function shall be the
address of a function that, when invoked, has the same effect as invoking the
closure type’s function call operator.
So I would file it as a CLANG bug.
As a work around for CLANG, you can convert it to a std::function as shown below:
struct foobar
{
template<typename... Args>
using fptr_t = void(*)(Args... args);
template<typename... Args>
operator std::function<void(Args...)>() const
{
return [](Args... args) {
//...
};
}
};
int main()
{
std::function<void(int)> f1 = foobar();
std::function<void(double, float)> f2 = foobar();
f1(1);
f2(2.0, 1.0f);
}
Live Demo