/// either by the user or implicitly.
unsigned DeclaredSpecialMembers : 6;
- /// \brief Whether an implicit copy constructor would have a const-qualified
- /// parameter.
- unsigned ImplicitCopyConstructorHasConstParam : 1;
+ /// \brief Whether an implicit copy constructor could have a const-qualified
+ /// parameter, for initializing virtual bases and for other subobjects.
+ unsigned ImplicitCopyConstructorCanHaveConstParamForVBase : 1;
+ unsigned ImplicitCopyConstructorCanHaveConstParamForNonVBase : 1;
/// \brief Whether an implicit copy assignment operator would have a
/// const-qualified parameter.
/// \brief Determine whether an implicit copy constructor for this type
/// would have a parameter with a const-qualified reference type.
bool implicitCopyConstructorHasConstParam() const {
- return data().ImplicitCopyConstructorHasConstParam;
+ return data().ImplicitCopyConstructorCanHaveConstParamForNonVBase &&
+ (isAbstract() ||
+ data().ImplicitCopyConstructorCanHaveConstParamForVBase);
}
/// \brief Determine whether this class has a copy constructor with
ToData.UserProvidedDefaultConstructor
= FromData.UserProvidedDefaultConstructor;
ToData.DeclaredSpecialMembers = FromData.DeclaredSpecialMembers;
- ToData.ImplicitCopyConstructorHasConstParam
- = FromData.ImplicitCopyConstructorHasConstParam;
+ ToData.ImplicitCopyConstructorCanHaveConstParamForVBase
+ = FromData.ImplicitCopyConstructorCanHaveConstParamForVBase;
+ ToData.ImplicitCopyConstructorCanHaveConstParamForNonVBase
+ = FromData.ImplicitCopyConstructorCanHaveConstParamForNonVBase;
ToData.ImplicitCopyAssignmentHasConstParam
= FromData.ImplicitCopyAssignmentHasConstParam;
ToData.HasDeclaredCopyConstructorWithConstParam
HasConstexprDefaultConstructor(false),
HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false),
UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0),
- ImplicitCopyConstructorHasConstParam(true),
+ ImplicitCopyConstructorCanHaveConstParamForVBase(true),
+ ImplicitCopyConstructorCanHaveConstParamForNonVBase(true),
ImplicitCopyAssignmentHasConstParam(true),
HasDeclaredCopyConstructorWithConstParam(false),
HasDeclaredCopyAssignmentWithConstParam(false), IsLambda(false),
// 'const B&' or 'const volatile B&' [...]
if (CXXRecordDecl *VBaseDecl = VBase.getType()->getAsCXXRecordDecl())
if (!VBaseDecl->hasCopyConstructorWithConstParam())
- data().ImplicitCopyConstructorHasConstParam = false;
+ data().ImplicitCopyConstructorCanHaveConstParamForVBase = false;
// C++1z [dcl.init.agg]p1:
// An aggregate is a class with [...] no virtual base classes
// In the definition of a constexpr constructor [...]
// -- the class shall not have any virtual base classes
data().DefaultedDefaultConstructorIsConstexpr = false;
+
+ // C++1z [class.copy]p8:
+ // The implicitly-declared copy constructor for a class X will have
+ // the form 'X::X(const X&)' if each potentially constructed subobject
+ // has a copy constructor whose first parameter is of type
+ // 'const B&' or 'const volatile B&' [...]
+ if (!BaseClassDecl->hasCopyConstructorWithConstParam())
+ data().ImplicitCopyConstructorCanHaveConstParamForVBase = false;
} else {
// C++ [class.ctor]p5:
// A default constructor is trivial [...] if:
// default constructor is constexpr.
if (!BaseClassDecl->hasConstexprDefaultConstructor())
data().DefaultedDefaultConstructorIsConstexpr = false;
+
+ // C++1z [class.copy]p8:
+ // The implicitly-declared copy constructor for a class X will have
+ // the form 'X::X(const X&)' if each potentially constructed subobject
+ // has a copy constructor whose first parameter is of type
+ // 'const B&' or 'const volatile B&' [...]
+ if (!BaseClassDecl->hasCopyConstructorWithConstParam())
+ data().ImplicitCopyConstructorCanHaveConstParamForNonVBase = false;
}
// C++ [class.ctor]p3:
if (!BaseClassDecl->hasCopyAssignmentWithConstParam())
data().ImplicitCopyAssignmentHasConstParam = false;
- // C++11 [class.copy]p8:
- // The implicitly-declared copy constructor for a class X will have
- // the form 'X::X(const X&)' if each direct [...] base class B of X
- // has a copy constructor whose first parameter is of type
- // 'const B&' or 'const volatile B&' [...]
- if (!BaseClassDecl->hasCopyConstructorWithConstParam())
- data().ImplicitCopyConstructorHasConstParam = false;
-
// A class has an Objective-C object member if... or any of its bases
// has an Objective-C object member.
if (BaseClassDecl->hasObjectMember())
// C++11 [class.copy]p8:
// The implicitly-declared copy constructor for a class X will have
- // the form 'X::X(const X&)' if [...] for all the non-static data
- // members of X that are of a class type M (or array thereof), each
- // such class type has a copy constructor whose first parameter is
- // of type 'const M&' or 'const volatile M&'.
+ // the form 'X::X(const X&)' if each potentially constructed subobject
+ // of a class type M (or array thereof) has a copy constructor whose
+ // first parameter is of type 'const M&' or 'const volatile M&'.
if (!FieldRec->hasCopyConstructorWithConstParam())
- data().ImplicitCopyConstructorHasConstParam = false;
+ data().ImplicitCopyConstructorCanHaveConstParamForNonVBase = false;
// C++11 [class.copy]p18:
// The implicitly-declared copy assignment oeprator for a class X will
const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CurGD.getDecl());
CXXDtorType DtorType = CurGD.getDtorType();
+ // For an abstract class, non-base destructors are never used (and can't
+ // be emitted in general, because vbase dtors may not have been validated
+ // by Sema), but the Itanium ABI doesn't make them optional and Clang may
+ // in fact emit references to them from other compilations, so emit them
+ // as functions containing a trap instruction.
+ if (DtorType != Dtor_Base && Dtor->getParent()->isAbstract()) {
+ llvm::CallInst *TrapCall = EmitTrapCall(llvm::Intrinsic::trap);
+ TrapCall->setDoesNotReturn();
+ TrapCall->setDoesNotThrow();
+ Builder.CreateUnreachable();
+ Builder.ClearInsertionPoint();
+ return;
+ }
+
Stmt *Body = Dtor->getBody();
if (Body)
incrementProfileCounter(Body);
DiagnoseUseOfDecl(Dtor, Location);
}
+ // We only potentially invoke the destructors of potentially constructed
+ // subobjects.
+ bool VisitVirtualBases = !ClassDecl->isAbstract();
+
llvm::SmallPtrSet<const RecordType *, 8> DirectVirtualBases;
// Bases.
const RecordType *RT = Base.getType()->getAs<RecordType>();
// Remember direct virtual bases.
- if (Base.isVirtual())
+ if (Base.isVirtual()) {
+ if (!VisitVirtualBases)
+ continue;
DirectVirtualBases.insert(RT);
+ }
CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(RT->getDecl());
// If our base class is invalid, we probably can't get its dtor anyway.
MarkFunctionReferenced(Location, Dtor);
DiagnoseUseOfDecl(Dtor, Location);
}
+
+ if (!VisitVirtualBases)
+ return;
// Virtual bases.
for (const auto &VBase : ClassDecl->vbases()) {
SpecialMemberDeletionInfo SMI(*this, MD, CSM, ICI, Diagnose);
// Per DR1611, do not consider virtual bases of constructors of abstract
- // classes, since we are not going to construct them. For assignment
- // operators, we only assign (and thus only consider) direct bases.
- if (SMI.visit(SMI.IsConstructor ? SMI.VisitPotentiallyConstructedBases :
- SMI.IsAssignment ? SMI.VisitDirectBases :
- SMI.VisitAllBases))
+ // classes, since we are not going to construct them.
+ // Per DR1658, do not consider virtual bases of destructors of abstract
+ // classes either.
+ // Per DR2180, for assignment operators we only assign (and thus only
+ // consider) direct bases.
+ if (SMI.visit(SMI.IsAssignment ? SMI.VisitDirectBases
+ : SMI.VisitPotentiallyConstructedBases))
return true;
if (SMI.shouldDeleteForAllConstMembers())
// destructor without a noexcept-specifier, is potentially-throwing if and
// only if any of the destructors for any of its potentially constructed
// subojects is potentially throwing.
- // FIXME: We should respect the first rule but ignore the "potentially
- // constructed" in the second rule to resolve a core issue (no number yet)
- // that would have us reject:
+ // FIXME: We respect the first rule but ignore the "potentially constructed"
+ // in the second rule to resolve a core issue (no number yet) that would have
+ // us reject:
// struct A { virtual void f() = 0; virtual ~A() noexcept(false) = 0; };
// struct B : A {};
// struct C : B { void f(); };
// ... due to giving B::~B() a non-throwing exception specification.
- // For now we don't implement either.
- Info.visit(Info.VisitAllBases);
+ Info.visit(Info.IsConstructor ? Info.VisitPotentiallyConstructedBases
+ : Info.VisitAllBases);
return Info.ExceptSpec;
}
Data.ComputedVisibleConversions = Record.readInt();
Data.UserProvidedDefaultConstructor = Record.readInt();
Data.DeclaredSpecialMembers = Record.readInt();
- Data.ImplicitCopyConstructorHasConstParam = Record.readInt();
+ Data.ImplicitCopyConstructorCanHaveConstParamForVBase = Record.readInt();
+ Data.ImplicitCopyConstructorCanHaveConstParamForNonVBase = Record.readInt();
Data.ImplicitCopyAssignmentHasConstParam = Record.readInt();
Data.HasDeclaredCopyConstructorWithConstParam = Record.readInt();
Data.HasDeclaredCopyAssignmentWithConstParam = Record.readInt();
// ComputedVisibleConversions is handled below.
MATCH_FIELD(UserProvidedDefaultConstructor)
OR_FIELD(DeclaredSpecialMembers)
- MATCH_FIELD(ImplicitCopyConstructorHasConstParam)
+ MATCH_FIELD(ImplicitCopyConstructorCanHaveConstParamForVBase)
+ MATCH_FIELD(ImplicitCopyConstructorCanHaveConstParamForNonVBase)
MATCH_FIELD(ImplicitCopyAssignmentHasConstParam)
OR_FIELD(HasDeclaredCopyConstructorWithConstParam)
OR_FIELD(HasDeclaredCopyAssignmentWithConstParam)
Record->push_back(Data.ComputedVisibleConversions);
Record->push_back(Data.UserProvidedDefaultConstructor);
Record->push_back(Data.DeclaredSpecialMembers);
- Record->push_back(Data.ImplicitCopyConstructorHasConstParam);
+ Record->push_back(Data.ImplicitCopyConstructorCanHaveConstParamForVBase);
+ Record->push_back(Data.ImplicitCopyConstructorCanHaveConstParamForNonVBase);
Record->push_back(Data.ImplicitCopyAssignmentHasConstParam);
Record->push_back(Data.HasDeclaredCopyConstructorWithConstParam);
Record->push_back(Data.HasDeclaredCopyAssignmentWithConstParam);
// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
// RUN: %clang_cc1 -std=c++1z -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+namespace dr1611 { // dr1611: dup 1658
+ struct A { A(int); };
+ struct B : virtual A { virtual void f() = 0; };
+ struct C : B { C() : A(0) {} void f(); };
+ C c;
+}
+
namespace dr1684 { // dr1684: 3.6
#if __cplusplus >= 201103L
struct NonLiteral { // expected-note {{because}}
b -= 1; // ok
}
}
+
+namespace dr1658 { // dr1658: 5
+ namespace DefCtor {
+ class A { A(); }; // expected-note 0-2{{here}}
+ class B { ~B(); }; // expected-note 0-2{{here}}
+
+ // The stars align! An abstract class does not construct its virtual bases.
+ struct C : virtual A { C(); virtual void foo() = 0; };
+ C::C() = default; // ok, not deleted, expected-error 0-1{{extension}}
+ struct D : virtual B { D(); virtual void foo() = 0; };
+ D::D() = default; // ok, not deleted, expected-error 0-1{{extension}}
+
+ // In all other cases, we are not so lucky.
+ struct E : A { E(); virtual void foo() = 0; };
+#if __cplusplus < 201103L
+ E::E() = default; // expected-error {{private default constructor}} expected-error {{extension}} expected-note {{here}}
+#else
+ E::E() = default; // expected-error {{would delete}} expected-note@-4{{inaccessible default constructor}}
+#endif
+ struct F : virtual A { F(); };
+#if __cplusplus < 201103L
+ F::F() = default; // expected-error {{private default constructor}} expected-error {{extension}} expected-note {{here}}
+#else
+ F::F() = default; // expected-error {{would delete}} expected-note@-4{{inaccessible default constructor}}
+#endif
+
+ struct G : B { G(); virtual void foo() = 0; };
+#if __cplusplus < 201103L
+ G::G() = default; // expected-error@-2 {{private destructor}} expected-error {{extension}} expected-note {{here}}
+#else
+ G::G() = default; // expected-error {{would delete}} expected-note@-4{{inaccessible destructor}}
+#endif
+ struct H : virtual B { H(); };
+#if __cplusplus < 201103L
+ H::H() = default; // expected-error@-2 {{private destructor}} expected-error {{extension}} expected-note {{here}}
+#else
+ H::H() = default; // expected-error {{would delete}} expected-note@-4{{inaccessible destructor}}
+#endif
+ }
+
+ namespace Dtor {
+ class B { ~B(); }; // expected-note 0-2{{here}}
+
+ struct D : virtual B { ~D(); virtual void foo() = 0; };
+ D::~D() = default; // ok, not deleted, expected-error 0-1{{extension}}
+
+ struct G : B { ~G(); virtual void foo() = 0; };
+#if __cplusplus < 201103L
+ G::~G() = default; // expected-error@-2 {{private destructor}} expected-error {{extension}} expected-note {{here}}
+#else
+ G::~G() = default; // expected-error {{would delete}} expected-note@-4{{inaccessible destructor}}
+#endif
+ struct H : virtual B { ~H(); };
+#if __cplusplus < 201103L
+ H::~H() = default; // expected-error@-2 {{private destructor}} expected-error {{extension}} expected-note {{here}}
+#else
+ H::~H() = default; // expected-error {{would delete}} expected-note@-4{{inaccessible destructor}}
+#endif
+ }
+
+ namespace MemInit {
+ struct A { A(int); }; // expected-note {{here}}
+ struct B : virtual A {
+ B() {}
+ virtual void f() = 0;
+ };
+ struct C : virtual A {
+ C() {} // expected-error {{must explicitly initialize}}
+ };
+ }
+
+ namespace CopyCtorParamType {
+ struct A { A(A&); };
+ struct B : virtual A { virtual void f() = 0; };
+ struct C : virtual A { virtual void f(); };
+ struct D : A { virtual void f() = 0; };
+
+ struct X {
+ friend B::B(const B&) throw();
+ friend C::C(C&);
+ friend D::D(D&);
+ };
+ }
+
+ namespace CopyCtor {
+ class A { A(const A&); A(A&&); }; // expected-note 0-4{{here}} expected-error 0-1{{extension}}
+
+ struct C : virtual A { C(const C&); C(C&&); virtual void foo() = 0; }; // expected-error 0-1{{extension}}
+ C::C(const C&) = default; // expected-error 0-1{{extension}}
+ C::C(C&&) = default; // expected-error 0-2{{extension}}
+
+ struct E : A { E(const E&); E(E&&); virtual void foo() = 0; }; // expected-error 0-1{{extension}}
+#if __cplusplus < 201103L
+ E::E(const E&) = default; // expected-error {{private copy constructor}} expected-error {{extension}} expected-note {{here}}
+ E::E(E&&) = default; // expected-error {{private move constructor}} expected-error 2{{extension}} expected-note {{here}}
+#else
+ E::E(const E&) = default; // expected-error {{would delete}} expected-note@-5{{inaccessible copy constructor}}
+ E::E(E&&) = default; // expected-error {{would delete}} expected-note@-6{{inaccessible move constructor}}
+#endif
+ struct F : virtual A { F(const F&); F(F&&); }; // expected-error 0-1{{extension}}
+#if __cplusplus < 201103L
+ F::F(const F&) = default; // expected-error {{private copy constructor}} expected-error {{extension}} expected-note {{here}}
+ F::F(F&&) = default; // expected-error {{private move constructor}} expected-error 2{{extension}} expected-note {{here}}
+#else
+ F::F(const F&) = default; // expected-error {{would delete}} expected-note@-5{{inaccessible copy constructor}}
+ F::F(F&&) = default; // expected-error {{would delete}} expected-note@-6{{inaccessible move constructor}}
+#endif
+ }
+
+ // assignment case is superseded by dr2180
+}
--- /dev/null
+// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++1z -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+
+namespace dr2180 { // dr2180: yes
+ class A {
+ A &operator=(const A &); // expected-note 0-2{{here}}
+ A &operator=(A &&); // expected-note 0-2{{here}} expected-error 0-1{{extension}}
+ };
+
+ struct B : virtual A {
+ B &operator=(const B &);
+ B &operator=(B &&); // expected-error 0-1{{extension}}
+ virtual void foo() = 0;
+ };
+#if __cplusplus < 201103L
+ B &B::operator=(const B&) = default; // expected-error {{private member}} expected-error {{extension}} expected-note {{here}}
+ B &B::operator=(B&&) = default; // expected-error {{private member}} expected-error 2{{extension}} expected-note {{here}}
+#else
+ B &B::operator=(const B&) = default; // expected-error {{would delete}} expected-note@-9{{inaccessible copy assignment}}
+ B &B::operator=(B&&) = default; // expected-error {{would delete}} expected-note@-10{{inaccessible move assignment}}
+#endif
+}
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -emit-llvm -o - | FileCheck %s --implicit-check-not=should_not_appear_in_output
struct Member { int x; Member(); Member(int); Member(const Member &); };
struct VBase { int x; VBase(); VBase(int); VBase(const VBase &); };
// CHECK: [[THIS:%.*]] = load [[B:%.*]]*, [[B:%.*]]**
// CHECK-NEXT: ret void
}
+
+// Ensure that we
+// a) emit the ABI-required but useless complete object and deleting destructor
+// symbols for an abstract class, and
+// b) do *not* emit references to virtual base destructors for an abstract class
+//
+// Our approach to this is to give these functions a body that simply traps.
+//
+// FIXME: We should ideally not create these symbols at all, but Clang can
+// actually generate references to them in other TUs in some cases, so we can't
+// stop emitting them without breaking ABI. See:
+//
+// https://github.com/itanium-cxx-abi/cxx-abi/issues/10
+namespace abstract {
+ // Note, the destructor of this class is not instantiated here.
+ template<typename T> struct should_not_appear_in_output {
+ ~should_not_appear_in_output() { int arr[-(int)sizeof(T)]; }
+ };
+
+ struct X { ~X(); };
+
+ struct A : virtual should_not_appear_in_output<int>, X {
+ virtual ~A() = 0;
+ };
+
+ // CHECK-LABEL: define void @_ZN8abstract1AD2Ev(
+ // CHECK: call {{.*}}@_ZN8abstract1XD2Ev(
+ // CHECK: ret
+
+ // CHECK-LABEL: define void @_ZN8abstract1AD1Ev(
+ // CHECK: call {{.*}}@llvm.trap(
+ // CHECK: unreachable
+
+ // CHECK-LABEL: define void @_ZN8abstract1AD0Ev(
+ // CHECK: call {{.*}}@llvm.trap(
+ // CHECK: unreachable
+ A::~A() {}
+
+ struct B : virtual should_not_appear_in_output<int>, X {
+ virtual void f() = 0;
+ ~B();
+ };
+
+ // CHECK-LABEL: define void @_ZN8abstract1BD2Ev(
+ // CHECK: call {{.*}}@_ZN8abstract1XD2Ev(
+ // CHECK: ret
+
+ // CHECK-LABEL: define void @_ZN8abstract1BD1Ev(
+ // CHECK: call {{.*}}@llvm.trap(
+ // CHECK: unreachable
+
+ // CHECK-NOT: @_ZN8abstract1BD0Ev(
+ B::~B() {}
+}
--- /dev/null
+// RUN: %clang_cc1 %s -triple=x86_64-linux-gnu -emit-llvm -std=c++11 -o - -fcxx-exceptions -fexceptions | FileCheck -check-prefix=CHECK %s
+
+struct A {
+ A();
+ A(const A&);
+ A(A&&);
+};
+struct B : virtual A {
+ virtual void f() = 0;
+};
+struct C : B {
+ void f();
+};
+
+// CHECK-DAG: define {{.*}} @_ZN1BC2Ev({{.*}} #[[NOUNWIND:[0-9]*]]
+C c1;
+// CHECK-DAG: define {{.*}} @_ZN1BC2ERKS_({{.*}} #[[NOUNWIND]]
+C c2(c1);
+// CHECK-DAG: define {{.*}} @_ZN1BC2EOS_({{.*}} #[[NOUNWIND]]
+C c3(static_cast<C&&>(c1));
+
+// CHECK-DAG: #[[NOUNWIND]] = {{{.*}} nounwind
} e;
};
}
+
+struct nothrow_t {} nothrow;
+void *operator new(decltype(sizeof(0)), nothrow_t) noexcept;
+
+namespace PotentiallyConstructed {
+ template<bool NE> struct A {
+ A() noexcept(NE);
+ A(const A&) noexcept(NE);
+ A(A&&) noexcept(NE);
+ A &operator=(const A&) noexcept(NE);
+ A &operator=(A&&) noexcept(NE);
+ ~A() noexcept(NE);
+ };
+
+ template<bool NE> struct B : virtual A<NE> {};
+
+ template<bool NE> struct C : virtual A<NE> {
+ virtual void f() = 0; // expected-note 2{{unimplemented}}
+ };
+
+ template<bool NE> struct D final : C<NE> {
+ void f();
+ };
+
+ template<typename T, bool A, bool B, bool C, bool D, bool E, bool F> void check() {
+ T *p = nullptr;
+ T &a = *p;
+ static_assert(noexcept(a = a) == D, "");
+ static_assert(noexcept(a = static_cast<T&&>(a)) == E, "");
+ static_assert(noexcept(delete &a) == F, ""); // expected-warning 2{{abstract}}
+
+ // These are last because the first failure here causes instantiation to bail out.
+ static_assert(noexcept(new (nothrow) T()) == A, ""); // expected-error 2{{abstract}}
+ static_assert(noexcept(new (nothrow) T(a)) == B, "");
+ static_assert(noexcept(new (nothrow) T(static_cast<T&&>(a))) == C, "");
+ }
+
+ template void check<A<false>, 0, 0, 0, 0, 0, 0>();
+ template void check<A<true >, 1, 1, 1, 1, 1, 1>();
+ template void check<B<false>, 0, 0, 0, 0, 0, 0>();
+ template void check<B<true >, 1, 1, 1, 1, 1, 1>();
+ template void check<C<false>, 1, 1, 1, 0, 0, 0>(); // expected-note {{instantiation}}
+ template void check<C<true >, 1, 1, 1, 1, 1, 1>(); // expected-note {{instantiation}}
+ template void check<D<false>, 0, 0, 0, 0, 0, 0>();
+ template void check<D<true >, 1, 1, 1, 1, 1, 1>();
+
+ // ... the above trick doesn't work for this case...
+ struct Cfalse : virtual A<false> {
+ virtual void f() = 0;
+
+ Cfalse() noexcept;
+ Cfalse(const Cfalse&) noexcept;
+ Cfalse(Cfalse&&) noexcept;
+ };
+ Cfalse::Cfalse() noexcept = default;
+ Cfalse::Cfalse(const Cfalse&) noexcept = default;
+ Cfalse::Cfalse(Cfalse&&) noexcept = default;
+}