SFINAE-friendly test for object deletability












3















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*);
^~~~~~~~









share|improve this question


















  • 1





    Why do you use this void_t definition? It is non-deduced context and thats the reason why GCC is complaining.

    – user8705939
    Nov 14 '18 at 5:10











  • The reason is that C++14 doesn't specify whether unused template arguments in an alias template are ignored or not. CWG1558 says they must not be ignored, but defining it like I have sidesteps the problem. Which part do you mean is non-deduced?

    – mkobierski
    Nov 14 '18 at 16:35








  • 1





    You should write @OZ17 , if you want to notify this commenter.

    – Michael Veksler
    Nov 14 '18 at 20:09











  • @MichaelVeksler thanks for the tip.

    – mkobierski
    Nov 14 '18 at 22:31
















3















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*);
^~~~~~~~









share|improve this question


















  • 1





    Why do you use this void_t definition? It is non-deduced context and thats the reason why GCC is complaining.

    – user8705939
    Nov 14 '18 at 5:10











  • The reason is that C++14 doesn't specify whether unused template arguments in an alias template are ignored or not. CWG1558 says they must not be ignored, but defining it like I have sidesteps the problem. Which part do you mean is non-deduced?

    – mkobierski
    Nov 14 '18 at 16:35








  • 1





    You should write @OZ17 , if you want to notify this commenter.

    – Michael Veksler
    Nov 14 '18 at 20:09











  • @MichaelVeksler thanks for the tip.

    – mkobierski
    Nov 14 '18 at 22:31














3












3








3


1






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*);
^~~~~~~~









share|improve this question














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*);
^~~~~~~~






c++ gcc template-meta-programming sfinae delete-operator






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 14 '18 at 4:49









mkobierskimkobierski

293




293








  • 1





    Why do you use this void_t definition? It is non-deduced context and thats the reason why GCC is complaining.

    – user8705939
    Nov 14 '18 at 5:10











  • The reason is that C++14 doesn't specify whether unused template arguments in an alias template are ignored or not. CWG1558 says they must not be ignored, but defining it like I have sidesteps the problem. Which part do you mean is non-deduced?

    – mkobierski
    Nov 14 '18 at 16:35








  • 1





    You should write @OZ17 , if you want to notify this commenter.

    – Michael Veksler
    Nov 14 '18 at 20:09











  • @MichaelVeksler thanks for the tip.

    – mkobierski
    Nov 14 '18 at 22:31














  • 1





    Why do you use this void_t definition? It is non-deduced context and thats the reason why GCC is complaining.

    – user8705939
    Nov 14 '18 at 5:10











  • The reason is that C++14 doesn't specify whether unused template arguments in an alias template are ignored or not. CWG1558 says they must not be ignored, but defining it like I have sidesteps the problem. Which part do you mean is non-deduced?

    – mkobierski
    Nov 14 '18 at 16:35








  • 1





    You should write @OZ17 , if you want to notify this commenter.

    – Michael Veksler
    Nov 14 '18 at 20:09











  • @MichaelVeksler thanks for the tip.

    – mkobierski
    Nov 14 '18 at 22:31








1




1





Why do you use this void_t definition? It is non-deduced context and thats the reason why GCC is complaining.

– user8705939
Nov 14 '18 at 5:10





Why do you use this void_t definition? It is non-deduced context and thats the reason why GCC is complaining.

– user8705939
Nov 14 '18 at 5:10













The reason is that C++14 doesn't specify whether unused template arguments in an alias template are ignored or not. CWG1558 says they must not be ignored, but defining it like I have sidesteps the problem. Which part do you mean is non-deduced?

– mkobierski
Nov 14 '18 at 16:35







The reason is that C++14 doesn't specify whether unused template arguments in an alias template are ignored or not. CWG1558 says they must not be ignored, but defining it like I have sidesteps the problem. Which part do you mean is non-deduced?

– mkobierski
Nov 14 '18 at 16:35






1




1





You should write @OZ17 , if you want to notify this commenter.

– Michael Veksler
Nov 14 '18 at 20:09





You should write @OZ17 , if you want to notify this commenter.

– Michael Veksler
Nov 14 '18 at 20:09













@MichaelVeksler thanks for the tip.

– mkobierski
Nov 14 '18 at 22:31





@MichaelVeksler thanks for the tip.

– mkobierski
Nov 14 '18 at 22:31












0






active

oldest

votes











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53293359%2fsfinae-friendly-test-for-object-deletability%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























0






active

oldest

votes








0






active

oldest

votes









active

oldest

votes






active

oldest

votes
















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53293359%2fsfinae-friendly-test-for-object-deletability%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

List item for chat from Array inside array React Native

Thiostrepton

Caerphilly