Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the proper usage of a constexpr standard functor?

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.

like image 735
OMGtechy Avatar asked Mar 12 '17 21:03

OMGtechy


1 Answers

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 type T, 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 of T would invoke a user-provided constructor of T (not inherited from a base class) or if
— each direct non-variant non-static data member M of T has a default member initializer or, if M is of class type X (or array thereof), X is const-default-constructible,
— if T is a union with at least one non-static data member, exactly one variant member has a default member initializer,
— if T 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 of T 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).

like image 78
Barry Avatar answered Nov 14 '22 07:11

Barry