Take the following code:
#include <iostream>
#include <functional>
template <template<typename> class Op>
auto applyOp(const uint8_t lhs, const uint8_t rhs) {
constexpr Op<uint8_t> op;
return op(lhs, rhs);
}
int main() {
std::cout << +applyOp<std::bit_and>(19, 180) << std::endl;
}
When using g++
, this compiles and runs just fine. However clang++
yields an error:
test.cpp:5:27: error: default initialization of an object of const type 'const bit_and<uint8_t>' (aka 'const bit_and<unsigned char>') without a user-provided default constructor
constexpr Op<uint8_t> op;
^
{}
test.cpp:11:19: note: in instantiation of function template specialization 'applyOp<std::bit_and>' requested here
std::cout << +applyOp<std::bit_and>(19, 180) << std::endl;
^
1 error generated.
So I took a look at the source code for bit_and:
// Copyright (C) 2001-2016 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/*
*
* Copyright (c) 1994
* Hewlett-Packard Company
*
* Permission to use, copy, modify, distribute and sell this software
* and its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation. Hewlett-Packard Company makes no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
*
* Copyright (c) 1996-1998
* Silicon Graphics Computer Systems, Inc.
*
* Permission to use, copy, modify, distribute and sell this software
* and its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation. Silicon Graphics makes no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*/
[ ... content omitted ... ]
template<typename _Arg1, typename _Arg2, typename _Result>
struct binary_function
{
/// @c first_argument_type is the type of the first argument
typedef _Arg1 first_argument_type;
/// @c second_argument_type is the type of the second argument
typedef _Arg2 second_argument_type;
/// @c result_type is the return type
typedef _Result result_type;
};
[ ... content omitted ... ]
#if __cplusplus > 201103L
template<typename _Tp = void>
struct bit_and;
[ ... content omitted ... ]
#endif
[ ... content omitted ... ]
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// DR 660. Missing Bitwise Operations.
template<typename _Tp>
struct bit_and : public binary_function<_Tp, _Tp, _Tp>
{
_GLIBCXX14_CONSTEXPR
_Tp
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x & __y; }
};
[ ... content omitted ... ]
For what I can tell, a default constructor should be generated here. Interestingly, the error message specifically asks for a "user-provided default constructor" rather than just a "default constructor".
Changing the offending line to use uniform initialization leads the code to work with both compilers:
- constexpr Op<uint8_t> op;
+ constexpr Op<uint8_t> op { };
My question is, is clang++
correct in demanding these extra braces, or is g++
correct in not?
Extra info
Compiling with g++
command:
g++ test.cpp -Werror -Wall -pedantic -std=c++14
Compiling with clang++
command:
clang++ test.cpp -Werror -Wall -pedantic -std=c++14
Running the application command:
./a.out
Location of bit_and
definition:
/usr/include/c++/6.2.0/bits/stl_function.h
Regarding the possible duplicate flag, I don't believe this is a duplicate because that question is asking why this rule exists, whilst my question was more about finding out about the rule in the first place and which compiler is correct in its enforcement. Furthermore, the answers to the alleged duplicate do not answer my question.
The original C++14 wording in [dcl.init] required that:
If a program calls for the default-initialization of an object of a
const
-qualified typeT
,T
shall be a class type with a user-provided default constructor.
Hence, simply having a default constructor is insufficient. It also needed to have been user-provided. Clang implemented this rule, which required constexpr Op<uint8_t> op{};
, but gcc did not - allowing default-initialization in this case.
However, this wording was revised just recently in p0490. The new wording, retroactively applied to C++14, reads:
A class type
T
is const-default-constructible if default-initialization ofT
would invoke a user-provided constructor ofT
(not inherited from a base class) or if
— each direct non-variant non-static data memberM
ofT
has a default member initializer or, ifM
is of class typeX
(or array thereof),X
is const-default-constructible,
— ifT
is a union with at least one non-static data member, exactly one variant member has a default member initializer,
— ifT
is a not a union, for each anonymous union member with at least one non-static data member (if any), exactly one non-static data member has a default member initializer, and each potentially constructed base class ofT
is const-default-constructible.If a program calls for the default-initialization of an object of a const-qualified type T, T shall be a const-default-constructible class type or array thereof.
Op<uint8_t>
is const-default-constructible (for the trivial reason that it has no non-static data members), so you can default-initialize it. Now, C++14 will allow:
constexpr Op<uint8_t> op;
Effectively, we went from a gcc bug (allowing default-initialization) to a clang bug (disallowing it).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With