I have an enum like this:
namespace api {
enum Operation {
INSERT =1,
UPDATE =2,
DELETE =3,
};
const Operation Operation_MIN = INSERT;
const Operation Operation_MAX = DELETE;
} // api
and I want to write some tests that use it:
#include "gtest.h"
class FWTest : public ::testing::Test {};
class FWTestPar : public FWTest, public ::testing::WithParamInterface<api::Operation>
{};
INSTANTIATE_TEST_CASE_P(Piping, FWTestPar, ::testing::Range(
api::Operation_MIN, api::Operation_MAX+1)
);
To be able to increment enums, I wrote this operator:
template <typename E>
constexpr typename std::enable_if<std::is_enum<E>::value, E>::type
operator+(E val, int step)
{
return static_cast<E>(static_cast<int>(val) + step);
}
This compiles fine with G++ from 4.8 to 6.2, but clang refuses to find operator+ :
clang++ -g -std=c++14 -Wall -pedantic -Wextra -Wformat=2 -I ~/workspace/zarquon -o "enum_inc_real" "enum_inc_real.cc" (in directory: /tmp)
In file included from enum_inc_real.cc:1:
third_party/gmock/gtest/gtest.h:10250:34: error: assigning to 'api::Operation' from incompatible type 'int'
for (T i = begin; i < end; i = i + step)
^ ~~~~~~~~
third_party/gmock/gtest/gtest.h:10191:33: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::CalculateEndIndex' requested here
step_(step), end_index_(CalculateEndIndex(begin, end, step)) {}
^
third_party/gmock/gtest/gtest.h:15815:11: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::RangeGenerator' requested here
new internal::RangeGenerator<T, IncrementT>(start, end, step));
^
third_party/gmock/gtest/gtest.h:15820:10: note: in instantiation of function template specialization 'testing::Range<api::Operation, int>' requested here
return Range(start, end, 1);
^
enum_inc_real.cc:34:55: note: in instantiation of function template specialization 'testing::Range<api::Operation>' requested here
INSTANTIATE_TEST_CASE_P(Piping, FWTestPar, ::testing::Range(
^
In file included from enum_inc_real.cc:1:
third_party/gmock/gtest/gtest.h:10213:14: error: assigning to 'api::Operation' from incompatible type 'int'
value_ = value_ + step_;
^ ~~~~~~~~~~~~~~
third_party/gmock/gtest/gtest.h:10204:5: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::Iterator::Advance' requested here
Iterator(const ParamGeneratorInterface<T>* base, T value, int index,
^
third_party/gmock/gtest/gtest.h:10195:16: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::Iterator::Iterator' requested here
return new Iterator(this, begin_, 0, step_);
^
third_party/gmock/gtest/gtest.h:10189:3: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::Begin' requested here
RangeGenerator(T begin, T end, IncrementT step)
^
third_party/gmock/gtest/gtest.h:15815:11: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::RangeGenerator' requested here
new internal::RangeGenerator<T, IncrementT>(start, end, step));
^
third_party/gmock/gtest/gtest.h:15820:10: note: in instantiation of function template specialization 'testing::Range<api::Operation, int>' requested here
return Range(start, end, 1);
^
enum_inc_real.cc:34:55: note: in instantiation of function template specialization 'testing::Range<api::Operation>' requested here
INSTANTIATE_TEST_CASE_P(Piping, FWTestPar, ::testing::Range(
^
If the templated operator+ is defined inside the api namespace, then clang++ also accepts this code.
So, which compiler is right?
Do I really have to drag operator+ into every single namespace that contains an enum I want to increment?
The odd thing is that gtest does something similar to this:
namespace testin_ {
namespace intl {
template<typename T>
class PII {};
template<typename T, typename Inc>
class RG{
public:
RG(T begin, T end, Inc s)
: begin_{begin}, end_{end}, step_{s}, end_index_{CalculateEndIndex(begin, end, s)}
{}
T begin() { return begin_; }
T end () { return end_; }
private:
class It : public PII<T> {
void advance() {
value_ = value_ + step_;
}
T value_;
int index_;
Inc step_;
};
static int CalculateEndIndex(const T& begin,
const T& end,
const Inc& step) {
int end_index = 0;
for (T i = begin; i < end; i = i + step)
end_index++;
return end_index;
}
const T begin_;
const T end_;
const Inc step_;
const int end_index_;
};
} // intl
template<typename T, typename Inc>
intl::RG<T, Inc> Rg(T start, T end, Inc step) {
return intl::RG<T, Inc>(start, end, step);
}
}
auto gen = testin_::Rg(api::INSERT, api::DELETE, 1);
bu that is accepted without complaints by clang++
Related
when reading the document of std::span, I see there is no method to remove the first element from the std::span<T>.
Can you suggest a way to solve my issue?
The large picture of my problem(I asked in another question: How to instantiatiate a std::basic_string_view with custom class T, I got is_trivial_v<_CharT> assert error) is that I would like to have a std::basic_string_view<Token>, while the Token is not a trivial class, so I can't use std::basic_string_view, and someone suggested me to use std::span<Token> instead.
Since the basic_string_view has a method named remove_prefix which remove the first element, while I also need such kinds of function because I would like to use std::span<Token> as a parser input, so the Tokens will be matched, and consumed one by one.
Thanks.
EDIT 2023-02-04
I try to derive a class named Span from std::span, and add the remove_prefix member function, but it looks like I still have build issues:
#include <string_view>
#include <vector>
#include <span>
// derived class, add remove_prefix function to std::span
template<typename T>
class Span : public std::span<T>
{
public:
// Inheriting constructors
using std::span<T>::span;
// add a public function which is similar to std::string_view::remove_prefix
constexpr void remove_prefix(std::size_t n) {
*this = subspan(n);
}
};
struct Token
{
Token(){};
Token(const Token& other)
{
lexeme = other.lexeme;
type = other.type;
}
std::string_view lexeme;
int type;
// equal operator
bool operator==(const Token& other)const {
return (this->lexeme == other.lexeme) ;
}
};
template <typename T>
struct Viewer;
template <>
struct Viewer<Token>
{
using type = Span<Token>; // std::span or derived class
};
template <>
struct Viewer<char>
{
using type = std::string_view;
};
template <typename T> using ViewerT = typename Viewer<T>::type;
template <typename T>
class Parser
{
using v = ViewerT<T>;
};
// a simple parser demo
template <typename Base, typename T>
struct parser_base {
using v = ViewerT<T>;
constexpr auto operator[](v& output) const noexcept;
};
template<typename T>
struct char_ final : public parser_base<char_<T>, T> {
using v = ViewerT<T>;
constexpr explicit char_(const T ch) noexcept
: ch(ch)
{}
constexpr inline bool visit(v& sv) const& noexcept {
if (!sv.empty() && sv.front() == ch) {
sv.remove_prefix(1);
return true;
}
return false;
}
private:
T ch;
};
template <typename Parser, typename T>
constexpr bool parse(Span<T> &input, Parser const& parser) noexcept {
return parser.visit(input);
}
int main()
{
Token kw_class;
kw_class.lexeme = "a";
std::vector<Token> token_stream;
token_stream.push_back(kw_class);
token_stream.push_back(kw_class);
token_stream.push_back(kw_class);
Span<Token> token_stream_view{&token_stream[0], 3};
auto p = char_(kw_class);
parse(token_stream_view, p);
return 0;
}
The build error looks like below:
[ 50.0%] g++.exe -Wall -std=c++20 -fexceptions -g -c F:\code\test_crtp_twoargs\main.cpp -o obj\Debug\main.o
F:\code\test_crtp_twoargs\main.cpp: In member function 'constexpr void Span<T>::remove_prefix(std::size_t)':
F:\code\test_crtp_twoargs\main.cpp:52:17: error: there are no arguments to 'subspan' that depend on a template parameter, so a declaration of 'subspan' must be available [-fpermissive]
52 | *this = subspan(n);
| ^~~~~~~
F:\code\test_crtp_twoargs\main.cpp:52:17: note: (if you use '-fpermissive', G++ will accept your code, but allowing the use of an undeclared name is deprecated)
F:\code\test_crtp_twoargs\main.cpp: In instantiation of 'constexpr void Span<T>::remove_prefix(std::size_t) [with T = Token; std::size_t = long long unsigned int]':
F:\code\test_crtp_twoargs\main.cpp:113:29: required from 'constexpr bool char_<T>::visit(v&) const & [with T = Token; v = Span<Token>]'
F:\code\test_crtp_twoargs\main.cpp:125:24: required from 'constexpr bool parse(Span<T>&, const Parser&) [with Parser = char_<Token>; T = Token]'
F:\code\test_crtp_twoargs\main.cpp:141:10: required from here
F:\code\test_crtp_twoargs\main.cpp:52:24: error: 'subspan' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
52 | *this = subspan(n);
| ~~~~~~~^~~
F:\code\test_crtp_twoargs\main.cpp:52:24: note: declarations in dependent base 'std::span<Token, 18446744073709551615>' are not found by unqualified lookup
F:\code\test_crtp_twoargs\main.cpp:52:24: note: use 'this->subspan' instead
F:\code\test_crtp_twoargs\main.cpp:52:15: error: no match for 'operator=' (operand types are 'Span<Token>' and 'std::span<Token, 18446744073709551615>')
52 | *this = subspan(n);
| ~~~~~~^~~~~~~~~~~~
F:\code\test_crtp_twoargs\main.cpp:44:7: note: candidate: 'constexpr Span<Token>& Span<Token>::operator=(const Span<Token>&)'
44 | class Span : public std::span<T>
| ^~~~
F:\code\test_crtp_twoargs\main.cpp:44:7: note: no known conversion for argument 1 from 'std::span<Token, 18446744073709551615>' to 'const Span<Token>&'
F:\code\test_crtp_twoargs\main.cpp:44:7: note: candidate: 'constexpr Span<Token>& Span<Token>::operator=(Span<Token>&&)'
F:\code\test_crtp_twoargs\main.cpp:44:7: note: no known conversion for argument 1 from 'std::span<Token, 18446744073709551615>' to 'Span<Token>&&'
Any idea on how to fix this issue?
Also, I don't know how to make a general parse function:
template <typename Parser, typename T>
constexpr bool parse(Span<T> &input, Parser const& parser) noexcept {
return parser.visit(input);
}
Currently, the first argument of the parse should be a Viewer like type?
EDIT2023-02-05
Change the function as below, the above code can build correctly. This is from Benjamin Buch's answer.
constexpr void remove_prefix(std::size_t n) {
auto& self = static_cast<std::span<T>&>(*this);
self = self.subspan(n);
}
There is still one thing remains: How to generalize the parse function to accept both input types of std::string_view and Span<Token>?
If I change the parse function to this:
template <typename Parser, typename T>
constexpr bool parse(ViewerT<T> &input, Parser const& parser) noexcept {
return parser.visit(input);
}
I got such compile error:
[ 50.0%] g++.exe -Wall -std=c++20 -fexceptions -g -c F:\code\test_crtp_twoargs\main.cpp -o obj\Debug\main.o
F:\code\test_crtp_twoargs\main.cpp: In function 'int main()':
F:\code\test_crtp_twoargs\main.cpp:143:24: error: no matching function for call to 'parse(Span<Token>&, char_<Token>&)'
143 | bool result = parse(token_stream_view, p);
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~
F:\code\test_crtp_twoargs\main.cpp:125:16: note: candidate: 'template<class Parser, class T> constexpr bool parse(ViewerT<T>&, const Parser&)'
125 | constexpr bool parse(ViewerT<T> &input, Parser const& parser) noexcept {
| ^~~~~
F:\code\test_crtp_twoargs\main.cpp:125:16: note: template argument deduction/substitution failed:
F:\code\test_crtp_twoargs\main.cpp:143:24: note: couldn't deduce template parameter 'T'
143 | bool result = parse(token_stream_view, p);
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~
Any ideas?
Thanks.
BTW: I have to explicitly instantiation of the parse function call like:
bool result = parse<decltype(p), Token>(token_stream_view, p);
to workaround this issue.
Call subspan with 1 as only (template) argument to get a new span, which doesn't contain the first element.
If you use a span with a static extend, you need a new variable because the data type changes by subspan.
#include <string_view>
#include <iostream>
#include <span>
int main() {
std::span<char const, 12> text_a("a test-span");
std::cout << std::string_view(text_a) << '\n';
std::span<char const, 10> text_b = text_a.subspan<2>();
std::cout << std::string_view(text_b) << '\n';
}
If you have a dynamic extend, you can assign the result to the original variable.
#include <string_view>
#include <iostream>
#include <span>
int main() {
std::span<char const> text("a test-span");
std::cout << std::string_view(text) << '\n';
text = text.subspan(2);
std::cout << std::string_view(text) << '\n';
}
The implementation of a modifying inplace subspan version is only possible for spans with a dynamic extend. It can be implemented as a free function.
#include <string_view>
#include <iostream>
#include <span>
template <typename T>
constexpr void remove_front(std::span<T>& self, std::size_t const n) noexcept {
self = self.subspan(n);
}
int main() {
std::span<char const> text("a test-span");
std::cout << std::string_view(text) << '\n';
remove_front(text, 2);
std::cout << std::string_view(text) << '\n';
}
You can use your own spans derived from std::span if you prefer the dot-call.
#include <string_view>
#include <iostream>
#include <span>
template <typename T>
struct my_span: std::span<T> {
using std::span<T>::span;
constexpr void remove_front(std::size_t const n) noexcept {
auto& self = static_cast<std::span<T>&>(*this);
self = self.subspan(n);
}
};
int main() {
my_span<char const> my_text("a test-span");
std::cout << std::string_view(my_text) << '\n';
my_text.remove_front(2);
std::cout << std::string_view(my_text) << '\n';
}
You can also write a wrapper class to call via dot syntax. This way you can additionally implement cascadable modification calls by always returning the a reference modifier class.
#include <string_view>
#include <iostream>
#include <span>
template <typename T>
class span_modifier {
public:
constexpr span_modifier(std::span<T>& span) noexcept: span_(span) {}
constexpr span_modifier& remove_front(std::size_t const n) noexcept {
span_ = span_.subspan(n);
return *this;
}
private:
std::span<T>& span_;
};
template <typename T>
constexpr span_modifier<T> modify(std::span<T>& span) noexcept {
return span;
}
int main() {
std::span<char const> text("a test-span");
std::cout << std::string_view(text) << '\n';
modify(text).remove_front(2).remove_front(5);
std::cout << std::string_view(text) << '\n';
}
Note I use the template function modify to create an object of the wrapper class, because the names of classes cannot be overloaded. Therefore class names should always be a bit more specific. The function modify can also be overloaded for other data types, which then return a different wrapper class. This results in a simple intuitive and consistent interface for modification wrappers.
You can write remove_prefix of your version,
template <typename T>
constexpr void remove_prefix(std::span<T>& sp, std::size_t n) {
sp = sp.subspan(n);
}
Demo
I'm working on an Advent of Code challenge (2021 day 18). Just as a test I tried compiling it on different compilers. While GCC (11.2) and MSVC (19.30) think it's fine, Clang (13.0.0) throws a list of errors. link to compiler explorer
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/alloc_traits.h:514:4: error: no matching function for call to 'construct_at'
std::construct_at(__p, std::forward<_Args>(__args)...);
^~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/vector.tcc:115:21: note: in instantiation of function template specialization 'std::allocator_traits<std::allocator<RegularNumber>>::construct<RegularNumber, int, Named<int, index_for_vector_of_RegularNumber> &, Named<int, index_for_vector_of_RegularNumber>>' requested here
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
^
<source>:115:19: note: in instantiation of function template specialization 'std::vector<RegularNumber>::emplace_back<int, Named<int, index_for_vector_of_RegularNumber> &, Named<int, index_for_vector_of_RegularNumber>>' requested here
regNumVec.emplace_back(*it - '0', leftRegNumIdx,
^
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/stl_construct.h:94:5: note: candidate template ignored: substitution failure [with _Tp = RegularNumber, _Args = <int, Named<int, index_for_vector_of_RegularNumber> &, Named<int, index_for_vector_of_RegularNumber>>]: no matching constructor for initialization of 'RegularNumber'
construct_at(_Tp* __location, _Args&&... __args)
^
What doesn't Clang understand what seems fine according to the other two? Is this a bug, or my mistake?
Here's the "minimalized" code:
#include <algorithm>
#include <concepts>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>
template <typename Type, typename Description>
class Named {
public:
using UnderlyingType = Type;
// clang-format off
constexpr Named()
noexcept(std::is_nothrow_default_constructible_v<Type>)
requires std::default_initializable<Type>
= default;
explicit constexpr Named(Type const &val)
noexcept(std::is_nothrow_copy_constructible_v<Type>)
requires std::copyable<Type>
: val_{val}
{}
explicit constexpr Named(Type&& val)
noexcept(std::is_nothrow_move_constructible_v<Type>)
requires std::movable<Type>
: val_{std::move(val)}
{}
constexpr operator Type() const&
noexcept(std::is_nothrow_copy_constructible_v<Type>)
requires std::copyable<Type>
{
return val_;
}
constexpr operator Type() const&&
noexcept(std::is_nothrow_move_constructible_v<Type>) // move assignable?
requires std::movable<Type>
{
return std::move(val_);
}
private:
Type val_;
};
// clang-format on
template <typename T>
auto readinputdata(std::istream &&is) {
auto data{std::vector<T>{}};
copy(std::istream_iterator<T>{is}, std::istream_iterator<T>{},
back_inserter(data));
return data;
}
static constexpr auto testData{
"[[[0,[5,8]],[[1,7],[9,6]]],[[4,[1,2]],[[1,4],2]]]\n"
"[[[5,[2,8]],4],[5,[[9,9],0]]]\n"
"[6,[[[6,2],[5,6]],[[7,6],[4,7]]]]\n"
"[[[6,[0,7]],[0,9]],[4,[9,[9,0]]]]\n"
"[[[7,[6,4]],[3,[1,3]]],[[[5,5],1],9]]\n"
"[[6,[[7,3],[3,2]]],[[[3,8],[5,7]],4]]\n"
"[[[[5,4],[7,7]],8],[[8,3],8]]\n"
"[[9,3],[[9,9],[6,[4,9]]]]\n"
"[[2,[[7,7],7]],[[5,8],[[9,3],[0,2]]]]\n"
"[[[[5,2],5],[8,[3,7]]],[[5,[7,5]],[4,4]]]\n"};
struct index_for_vector_of_Pair;
using PairIndex = Named<int, index_for_vector_of_Pair>;
struct index_for_vector_of_RegularNumber;
using RegNumIndex = Named<int, index_for_vector_of_RegularNumber>;
struct RegularNumber {
using IndexType = RegNumIndex;
int value{};
RegNumIndex leftIdx{}, rightIdx{};
};
using IndexVariant = std::variant<PairIndex, RegNumIndex>;
struct Pair {
using IndexType = PairIndex;
PairIndex parent{};
IndexVariant leftIdx{}, rightIdx{};
};
static auto pairVec{std::vector<Pair>{}};
static auto regNumVec{std::vector<RegularNumber>{}};
// used for converting the input strings to pairs
static auto leftRegNumIdx{RegNumIndex{-1}};
static constexpr auto invalid_index{-1};
template <typename Type>
auto get_last_index(std::vector<Type> const &vec) {
using Index = typename Type::IndexType;
return Index{static_cast<typename Index::UnderlyingType>(size(vec))};
}
auto generate_pair(std::string::const_iterator &it, PairIndex parent)
-> PairIndex;
auto generate_side(std::string::const_iterator &it, PairIndex pairIdx)
-> IndexVariant {
if (*it == '[') {
return generate_pair(it, pairIdx);
} else {
regNumVec.emplace_back(*it - '0', leftRegNumIdx, // error on this line
RegNumIndex{invalid_index});
auto const litIdx{get_last_index(regNumVec)};
if (leftRegNumIdx != invalid_index) {
regNumVec[leftRegNumIdx].rightIdx = litIdx;
}
leftRegNumIdx = litIdx;
return litIdx;
};
}
auto generate_pair(std::string::const_iterator &it,
PairIndex parent = PairIndex{invalid_index}) -> PairIndex {
pairVec.emplace_back();
auto const pairIdx{get_last_index(pairVec)};
auto &pair{pairVec.back()};
pair.parent = parent;
++it; // skip [
pair.leftIdx = generate_side(it, pairIdx);
++it; // skip comma
pair.rightIdx = generate_side(it, pairIdx);
++it; // skip ]
return pairIdx;
}
int main() {
auto const snailFishNumbers{readinputdata<std::string>( //
std::stringstream{testData} //
)};
auto const outerPairIdxs{[&] {
auto outerPairIdxs{std::vector<PairIndex>{}};
outerPairIdxs.reserve(size(snailFishNumbers));
transform(cbegin(snailFishNumbers), cend(snailFishNumbers),
back_inserter(outerPairIdxs), [](auto const &str) {
auto it{cbegin(str)}; // need lvalue
return generate_pair(it);
});
return outerPairIdxs;
}()};
return 0;
}
Your type is an aggregate
struct RegularNumber {
using IndexType = RegNumIndex;
int value{};
RegNumIndex leftIdx{}, rightIdx{};
};
which construct_at tries to initialize with round parenthesis
::new(std::declval<void*>()) T(std::declval<Args>()...).
However, an aggregate needs a {} initializer, until the compiler implements P0960 Allow initializing aggregates from a parenthesized list of values
And Clang just isn't there yet. Compiler support for C++20
Forward_iterator compiles fine!
#include <concepts>
#include <iterator>
// #include <iterator_concept>
using namespace std;
template<typename T>
// struct my_iterator : std::forward_iterator_tag{
struct my_iterator : std::bidirectional_iterator_tag {
T& operator*()const{
return *pointer;
}
my_iterator& operator++(){
++pointer;
return *this;
}
my_iterator operator++(int){
auto copy = *this;
++pointer;
return copy;
}
// my_iterator& operator--(){
// --pointer;
// return *this;
// }
// my_iterator operator--(int){
// auto copy = *this;
// --pointer;
// return copy;
// }
using difference_type = ptrdiff_t;
using value_type = T;
// auto operator<=>(const my_iterator&) const = default;
bool operator==(const my_iterator& other)const{
return pointer == other.pointer;
}
bool operator!=(const my_iterator& other)const{
return pointer != other.pointer;
}
private:
T*pointer;
};
// template<class T> constexpr bool always_false = false;
// static_assert(always_false<std::iter_reference_t<my_iterator<int>>>, "asd");
// typedef typename std::iter_reference_t<my_iterator<int>>::something_made_up X;
// static_assert(std::weakly_incrementable<my_iterator<int>>);
// static_assert(std::equality_comparable<my_iterator<int>>);
static_assert(std::forward_iterator<my_iterator<int>>);
// static_assert(std::bidirectional_iterator<my_iterator<int>>);
int main()
{
my_iterator<int> i1;
}
live snippet godbolt
but bidirectional does not. I tried g++ v10 and clang++ v11. Both give nearly the same compile time error.
#include <concepts>
#include <iterator>
using namespace std;
template<typename T>
// struct my_iterator : std::forward_iterator_tag{
struct my_iterator : std::bidirectional_iterator_tag {
T& operator*()const{
return *pointer;
}
my_iterator& operator++(){
++pointer;
return *this;
}
my_iterator operator++(int){
auto copy = *this;
++pointer;
return copy;
}
my_iterator& operator--(){
--pointer;
return *this;
}
my_iterator operator--(int){
auto copy = *this;
--pointer;
return copy;
}
using difference_type = ptrdiff_t;
using value_type = T;
// auto operator<=>(const my_iterator&) const = default; // Why this does not work
bool operator==(const my_iterator& other)const{
return pointer == other.pointer;
}
bool operator!=(const my_iterator& other)const{
return pointer != other.pointer;
}
private:
T*pointer;
};
// template<class T> constexpr bool always_false = false;
// static_assert(always_false<std::iter_reference_t<my_iterator<int>>>, "asd");
// typedef typename std::iter_reference_t<my_iterator<int>>::something_made_up X;
// static_assert(std::weakly_incrementable<my_iterator<int>>);
// static_assert(std::equality_comparable<my_iterator<int>>);
static_assert(std::forward_iterator<my_iterator<int>>);
// static_assert(std::bidirectional_iterator<my_iterator<int>>);
int main()
{
my_iterator<int> i1;
}
live
In file included from <source>:2:
In file included from /opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/iterator:61:
In file included from /opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/stl_iterator_base_types.h:71:
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:409:52: error: ambiguous partial specializations of '__cat<my_iterator<int>>'
using iterator_category = typename __detail::__cat<_Iterator>::type;
^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/stl_iterator_base_types.h:178:14: note: in instantiation of template class 'std::__iterator_traits<my_iterator<int>, void>' requested here
: public __iterator_traits<_Iterator> { };
^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:182:71: note: in instantiation of template class 'std::iterator_traits<my_iterator<int>>' requested here
= __is_base_of(__iterator_traits<_Iter, void>, iterator_traits<_Iter>);
^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:182:4: note: while substituting template arguments into constraint expression here
= __is_base_of(__iterator_traits<_Iter, void>, iterator_traits<_Iter>);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:189:16: note: while checking the satisfaction of concept '__primary_traits_iter<my_iterator<int>>' requested here
requires __primary_traits_iter<_Iter>
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:189:16: note: while substituting template arguments into constraint expression here
requires __primary_traits_iter<_Iter>
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:195:7: note: (skipping 10 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
using __iter_traits = typename __iter_traits_impl<_Iter, _Tp>::type;
^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:564:30: note: while checking the satisfaction of concept 'input_or_output_iterator<my_iterator<int>>' requested here
concept input_iterator = input_or_output_iterator<_Iter>
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:564:30: note: while substituting template arguments into constraint expression here
concept input_iterator = input_or_output_iterator<_Iter>
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:575:32: note: while checking the satisfaction of concept 'input_iterator<my_iterator<int>>' requested here
concept forward_iterator = input_iterator<_Iter>
^~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:575:32: note: while substituting template arguments into constraint expression here
concept forward_iterator = input_iterator<_Iter>
^~~~~~~~~~~~~~~~~~~~~
<source>:52:15: note: while checking the satisfaction of concept 'forward_iterator<my_iterator<int>>' requested here
static_assert(std::forward_iterator<my_iterator<int>>);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:369:14: note: partial specialization matches [with _Iter = my_iterator<int>]
struct __cat<_Iter>
^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:375:14: note: partial specialization matches [with _Iter = my_iterator<int>]
struct __cat<_Iter>
^
<source>:52:1: error: static_assert failed
static_assert(std::forward_iterator<my_iterator<int>>);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:52:20: note: because 'my_iterator<int>' does not satisfy 'forward_iterator'
static_assert(std::forward_iterator<my_iterator<int>>);
^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:575:32: note: because 'my_iterator<int>' does not satisfy 'input_iterator'
concept forward_iterator = input_iterator<_Iter>
^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:564:30: note: because 'my_iterator<int>' does not satisfy 'input_or_output_iterator'
concept input_iterator = input_or_output_iterator<_Iter>
^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:544:5: note: because 'my_iterator<int>' does not satisfy 'weakly_incrementable'
&& weakly_incrementable<_Iter>;
^
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/iterator_concepts.h:198:7: note: because 'typename iter_difference_t<_Iter>' would be invalid: no type named 'difference_type' in 'std::iterator_traits<my_iterator<int>>'
using __iter_diff_t = typename
^
2 errors generated.
Compiler returned: 1
x86-64 clang 11.0.0
1
More questions:
Why auto operator<=>(const my_iterator&) const = default; does not provide == and !=?
May be it does but static_assert(std::forward_iterator<my_iterator<int>>); want operator== to be explicit.
Is there a way to explicitly say this struct should meet the requirements and constraints of the concept like
template<typename T>
struct my_iterator implements forward_iterator {};
Currently I need to check it with static_assert(std::forward_iterator<my_iterator<int>>);.
This:
struct my_iterator : std::bidirectional_iterator_tag {
is not how you indicate the iterator category for an iterator. You don't inherit from a tag type, you have it as a member type alias.
Like so:
struct iterator {
using iterator_category = std::bidirectional_iterator_tag;
};
Although this shouldn't actually be necessary anyway. Even without this tag (and definitely without inheritance), this iterator should count as a bidirectional iterator since you satisfy the other requirements. It's actually a libstdc++ bug that it doesn't compile at that point.
Why auto operator<=>(const my_iterator&) const = default; does not provide == and !=?
It does. It's just that you were inheriting from a type (the iterator tag) that was not comparable, so the defaulted comparisons were defined as deleted.
Is there a way to explicitly say this struct should meet the requirements and constraints of the concept
There is not.
I've come across the pattern matching proposal in C++, and I tried to compile Example 4.6 about expression trees.
Here's the extracted code:
#include <memory>
#include <variant>
struct Expr;
struct Neg {
std::shared_ptr<Expr> expr;
};
struct Add {
std::shared_ptr<Expr> lhs, rhs;
};
struct Mul {
std::shared_ptr<Expr> lhs, rhs;
};
struct Expr : std::variant<int, Neg, Add, Mul> {
using variant::variant;
};
namespace std {
template <>
struct variant_size<Expr> : variant_size<Expr::variant> {};
template <std::size_t I>
struct variant_alternative<I, Expr> : variant_alternative<I, Expr::variant> {};
}
int eval(const Expr& expr) {
struct visitor {
int operator()(int i) const {return i;}
int operator()(const Neg& n) const {return -eval(*n.expr);}
int operator()(const Add& a) const {return eval(*a.lhs) + eval(*a.rhs);}
int operator()(const Mul& m) const {// Optimize multiplication by 0.
if (int* i = std::get_if<int>(m.lhs.get()); i && *i == 0) {
return 0;
}
if (int* i = std::get_if<int>(m.rhs.get()); i && *i == 0) {
return 0;
}
return eval(*m.lhs) * eval(*m.rhs);}
};
return std::visit(visitor{}, expr);
}
The code above gives 4 compilation errors, with:
clang++ -std=c++17 main.cpp
In file included from main.cpp:6:
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:778:6: error: implicit instantiation of undefined template 'std::__detail::__variant::_Extra_visit_slot_needed<int, const Expr &>::_Variant_never_valueless<Expr>'
&& !_Variant_never_valueless<__remove_cvref_t<_Variant>>::value;
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:809:44: note: in instantiation of static data member 'std::__detail::__variant::_Extra_visit_slot_needed<int, const Expr &>::value' requested here
_Extra_visit_slot_needed<_Ret, _Variant>::value ? 1 : 0;
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:821:53: note: in instantiation of static data member 'std::__detail::__variant::_Multi_array<int (*)(visitor &&, const Expr &), 4>::__do_cookie' requested here
_Multi_array<_Tp, __rest...> _M_arr[__first + __do_cookie];
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1016:36: note: in instantiation of template class 'std::__detail::__variant::_Multi_array<int (*)(visitor &&, const Expr &), 4>' requested here
static constexpr _Array_type _S_vtable
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1638:55: note: in instantiation of template class 'std::__detail::__variant::__gen_vtable<true, int, visitor &&, const Expr &>' requested here
constexpr auto& __vtable = __detail::__variant::__gen_vtable<
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1654:14: note: in instantiation of function template specialization 'std::__do_visit<false, true, visitor, const Expr &>' requested here
return __do_visit(std::forward<_Visitor>(__visitor),
^
main.cpp:48:15: note: in instantiation of function template specialization 'std::visit<visitor, const Expr &>' requested here
return std::visit(visitor{}, expr);
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:769:34: note: template is declared here
template <typename> struct _Variant_never_valueless;
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:809:2: error: constexpr variable '__do_cookie' must be initialized by a constant expression
_Extra_visit_slot_needed<_Ret, _Variant>::value ? 1 : 0;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:821:53: note: in instantiation of static data member 'std::__detail::__variant::_Multi_array<int (*)(visitor &&, const Expr &), 4>::__do_cookie' requested here
_Multi_array<_Tp, __rest...> _M_arr[__first + __do_cookie];
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1016:36: note: in instantiation of template class 'std::__detail::__variant::_Multi_array<int (*)(visitor &&, const Expr &), 4>' requested here
static constexpr _Array_type _S_vtable
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1638:55: note: in instantiation of template class 'std::__detail::__variant::__gen_vtable<true, int, visitor &&, const Expr &>' requested here
constexpr auto& __vtable = __detail::__variant::__gen_vtable<
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1654:14: note: in instantiation of function template specialization 'std::__do_visit<false, true, visitor, const Expr &>' requested here
return __do_visit(std::forward<_Visitor>(__visitor),
^
main.cpp:48:15: note: in instantiation of function template specialization 'std::visit<visitor, const Expr &>' requested here
return std::visit(visitor{}, expr);
^
In file included from main.cpp:6:
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:821:36: error: fields must have a constant size: 'variable length array in structure' extension will never be supported
_Multi_array<_Tp, __rest...> _M_arr[__first + __do_cookie];
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1016:36: note: in instantiation of template class 'std::__detail::__variant::_Multi_array<int (*)(visitor &&, const Expr &), 4>' requested here
static constexpr _Array_type _S_vtable
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1638:55: note: in instantiation of template class 'std::__detail::__variant::__gen_vtable<true, int, visitor &&, const Expr &>' requested here
constexpr auto& __vtable = __detail::__variant::__gen_vtable<
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1654:14: note: in instantiation of function template specialization 'std::__do_visit<false, true, visitor, const Expr &>' requested here
return __do_visit(std::forward<_Visitor>(__visitor),
^
main.cpp:48:15: note: in instantiation of function template specialization 'std::visit<visitor, const Expr &>' requested here
return std::visit(visitor{}, expr);
^
In file included from main.cpp:6:
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1016:36: error: constexpr variable '_S_vtable' must be initialized by a constant expression
static constexpr _Array_type _S_vtable
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1640:45: note: in instantiation of static data member 'std::__detail::__variant::__gen_vtable<true, int, visitor &&, const Expr &>::_S_vtable' requested here
_Result_type, _Visitor&&, _Variants&&...>::_S_vtable;
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/variant:1654:14: note: in instantiation of function template specialization 'std::__do_visit<false, true, visitor, const Expr &>' requested here
return __do_visit(std::forward<_Visitor>(__visitor),
^
main.cpp:48:15: note: in instantiation of function template specialization 'std::visit<visitor, const Expr &>' requested here
return std::visit(visitor{}, expr);
^
4 errors generated.
How to make the code above to compile?
Basically, the question is how to have recursive types with std::variant and std::shared_ptr for incomplete types.
The command clang++ --version gives:
clang version 9.0.0-2 (tags/RELEASE_900/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
The following code is a minimum working (or perhaps non-working) example.
What it does is basically encapsulates a bunch of std::map structures as private members in a base class. To avoid writing a lot of setters and getters, they are implemented as template functions.
// test.cpp
#include <map>
#include <iostream>
enum class E0
{
F0, F1, F2,
};
The declaration of the base class.
using std::map;
class P_base
{
private:
map<E0, int> m_imap;
// ...
// ... Other std::map members with different key types and value types.
public:
map<E0, int> & imap;
// ...
// ... Other std::map references.
P_base() : imap(m_imap) {}
template<typename map_type, typename key_type, typename val_type>
void set(map_type & m, const key_type & k, const val_type & v)
{
m[k] = v;
}
template<typename map_type, typename key_type>
auto access_to_map(const map_type & m, const key_type & k) -> decltype(m.at(k))
{
return m.at(k);
}
};
class P : private P_base
{
public:
decltype(P_base::imap) & imap;
P() : P_base(), imap(P_base::imap) {}
template<typename map_type, typename key_type, typename val_type>
void set(map_type & m, const key_type & k, const val_type & v)
{
P_base::set(m, k, v);
}
template<typename map_type, typename key_type>
auto access_to_map(const map_type & m, const key_type & k) -> decltype(P_base::access_to_map(m, k))
{
return P_base::access_to_map(m, k);
}
};
main
int main(int argc, const char * argv[])
{
using std::cout;
using std::endl;
P op;
op.set(op.imap, E0::F0, 100);
op.set(op.imap, E0::F1, 101);
op.set(op.imap, E0::F2, 102);
cout << op.access_to_map(op.imap, E0::F1) << endl;
}
$ clang++ -std=c++11 test.cpp && ./a.out
101
But if I compile it with intel compiler (icpc version 15.0.3 (gcc version 5.1.0 compatibility)), the compiler gives me this error message (which I don't undertand at all, especially when clang will compile the code):
$ icpc -std=c++ test.cpp && ./a.out
test.cpp(67): error: no instance of function template "P::access_to_map" matches the argument list
argument types are: (std::__1::map<E0, int, std::__1::less<E0>, std::__1::allocator<std::__1::pair<const E0, int>>>, E0)
object type is: P
cout << op.access_to_map(op.imap, E0::F1) << endl;
And it also confuses me by not complaining about the set function.
Does anyone have any idea what is going on here?
Note: My answer applies to g++ - hopefully it's the same as icc.
Here is a smaller test case:
struct Base
{
int func(int t) { return t; }
};
struct Der : Base
{
template<typename T>
auto f(T t) -> decltype(Base::func(t))
{
return t;
}
};
int main(){ Der d; d.f(5); }
The error is:
mcv.cc: In function 'int main()':
mcv.cc:16:25: error: no matching function for call to 'Der::f(int)'
int main(){ Der d; d.f(5); }
^
mcv.cc:16:25: note: candidate is:
mcv.cc:9:7: note: template<class T> decltype (t->Base::func()) Der::f(T)
auto f(T t) -> decltype(Base::func(t))
^
mcv.cc:9:7: note: template argument deduction/substitution failed:
mcv.cc: In substitution of 'template<class T> decltype (t->Base::func()) Der::f(T) [with T = int]':
mcv.cc:16:25: required from here
mcv.cc:9:38: error: cannot call member function 'int Base::func(int)' without object
auto f(T t) -> decltype(Base::func(t))
This can be fixed by changing decltype(Base::func(t)) to decltype(this->Base::func(t)). A corresponding fix fixes your code sample, for me.
Apparently, the compiler doesn't consider that Base::func(t) should be called with *this as hidden argument. I don't know if this is a g++ bug, or if clang is going beyond the call of duty.
Note that in C++14, since the function has a single return statement, the trailing return type can be omitted entirely:
template<typename T>
auto f(T t)
{
return t;
}