Skip to content

Fix expected: operator!= SFINAE, [[nodiscard]], constexpr, and add tests - #44

Merged
yaito3014 merged 4 commits into
expectedfrom
claude/review-43-oubmru
Jul 16, 2026
Merged

Fix expected: operator!= SFINAE, [[nodiscard]], constexpr, and add tests#44
yaito3014 merged 4 commits into
expectedfrom
claude/review-43-oubmru

Conversation

@yaito3014

Copy link
Copy Markdown
Owner

Summary

Review fixes for the expected implementation in #43.

  • Fix operator!= SFINAE: The two operator!= overloads (expected<T1,E1> vs expected<T2,E2> and the void specialization) checked != convertibility in their SFINAE constraints, but the body uses !(lhs == rhs). Changed to check == convertibility, fixing a conformance issue for types that define == but not !=.
  • Replace [[nodiscard]] with YK_POLYFILL_NODISCARD: All 40 raw [[nodiscard]] usages replaced with the project's portability macro for C++11 compatibility.
  • Fix constexpr mismatch on const&& helpers: base_get_value() const&& and base_get_error() const&& were marked YK_POLYFILL_CXX14_CONSTEXPR but called from constexpr callers (operator*() const&&, error() const&&). Since const member functions are valid constexpr in C++11, changed them to plain constexpr.
  • Remove unused times_two test helper.
  • Add missing tests: rvalue overloads of value()/error()/operator*(), transform with a function pointer, and monadic rvalue overloads (and_then/or_else/transform/transform_error on &&).

Test plan

  • C++11 tests pass (163 assertions in 19 test cases)
  • C++20 tests pass (21 assertions in 3 test cases)

Generated by Claude Code

claude added 3 commits July 16, 2026 13:09
…SCARD

- operator!= for expected<T1,E1> vs expected<T2,E2> and the void
  specialization checked != convertibility in SFINAE but the body uses
  !(lhs == rhs), so the constraint should check == convertibility instead.
  This fixes a conformance issue for types that define == but not !=.

- Replace all raw [[nodiscard]] with YK_POLYFILL_NODISCARD for C++11
  compatibility, matching the project's feature-gating convention.

- Remove unused times_two helper in test/cxx11/expected.cpp.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0199s51dz1Jety9DKdnA9JRY
…orm, monadic rvalue overloads

- Test value()/error()/operator*() rvalue overloads for correct reference
  types and actual value forwarding (both non-void and void expected)
- Test value() rvalue throwing bad_expected_access with the moved error
- Test transform with a function pointer (times_two)
- Test and_then/or_else/transform/transform_error rvalue overloads

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0199s51dz1Jety9DKdnA9JRY
The const&& overloads are const member functions, so they are valid
constexpr in C++11. They were incorrectly marked YK_POLYFILL_CXX14_CONSTEXPR,
creating a mismatch with their constexpr callers (operator*() const&&,
error() const&&). Change them to plain constexpr.

The non-const && overloads correctly remain YK_POLYFILL_CXX14_CONSTEXPR
since non-const constexpr member functions require C++14.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0199s51dz1Jety9DKdnA9JRY

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the expected polyfill by replacing standard [[nodiscard]] attributes with YK_POLYFILL_NODISCARD, correcting type trait checks in operator!= overloads, and refining constexpr specifiers for const rvalue accessors. It also adds comprehensive unit tests for rvalue observers and monadic operations. The reviewer suggests casting *this directly to base_type const&& instead of using std::move on a base_type const& cast in the const rvalue accessors. This avoids using std::move, which is not guaranteed to be constexpr in C++11, thereby ensuring strict C++11 compatibility.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread include/yk/polyfill/expected.hpp Outdated
// rvalue accessors on the storage (private base), reached through explicit std::move.
YK_POLYFILL_CXX14_CONSTEXPR T&& base_get_value() && noexcept { return std::move(static_cast<base_type&>(*this).get_value()); }
YK_POLYFILL_CXX14_CONSTEXPR T const&& base_get_value() const&& noexcept { return std::move(static_cast<base_type const&>(*this).get_value()); }
constexpr T const&& base_get_value() const&& noexcept { return std::move(static_cast<base_type const&>(*this).get_value()); }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Instead of casting *this to base_type const& and then calling std::move on the result, you can cast *this directly to base_type const&&. This will invoke the rvalue overload get_value() const&& on the base class, which already returns T const&& directly.

This is cleaner and ensures strict C++11 constexpr compatibility, as std::move is not standard-mandated to be constexpr in C++11.

  constexpr T const&& base_get_value() const&& noexcept { return static_cast<base_type const&&>(*this).get_value(); }

Comment thread include/yk/polyfill/expected.hpp Outdated
constexpr T const&& base_get_value() const&& noexcept { return std::move(static_cast<base_type const&>(*this).get_value()); }
YK_POLYFILL_CXX14_CONSTEXPR E&& base_get_error() && noexcept { return std::move(static_cast<base_type&>(*this).get_error()); }
YK_POLYFILL_CXX14_CONSTEXPR E const&& base_get_error() const&& noexcept { return std::move(static_cast<base_type const&>(*this).get_error()); }
constexpr E const&& base_get_error() const&& noexcept { return std::move(static_cast<base_type const&>(*this).get_error()); }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Instead of casting *this to base_type const& and then calling std::move on the result, you can cast *this directly to base_type const&&. This will invoke the rvalue overload get_error() const&& on the base class, which already returns E const&& directly.

This is cleaner and ensures strict C++11 constexpr compatibility, as std::move is not standard-mandated to be constexpr in C++11.

  constexpr E const&& base_get_error() const&& noexcept { return static_cast<base_type const&&>(*this).get_error(); }

Comment thread include/yk/polyfill/expected.hpp Outdated
private:
YK_POLYFILL_CXX14_CONSTEXPR E&& base_get_error() && noexcept { return std::move(static_cast<base_type&>(*this).get_error()); }
YK_POLYFILL_CXX14_CONSTEXPR E const&& base_get_error() const&& noexcept { return std::move(static_cast<base_type const&>(*this).get_error()); }
constexpr E const&& base_get_error() const&& noexcept { return std::move(static_cast<base_type const&>(*this).get_error()); }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Instead of casting *this to base_type const& and then calling std::move on the result, you can cast *this directly to base_type const&&. This will invoke the rvalue overload get_error() const&& on the base class, which already returns E const&& directly.

This is cleaner and ensures strict C++11 constexpr compatibility, as std::move is not standard-mandated to be constexpr in C++11.

  constexpr E const&& base_get_error() const&& noexcept { return static_cast<base_type const&&>(*this).get_error(); }

Cast *this directly to base_type const&& (or base_type&&) to invoke the
rvalue-qualified get_value()/get_error() overloads, instead of calling
the lvalue overload and then std::move on the result. Cleaner and avoids
reliance on std::move being constexpr in C++11.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0199s51dz1Jety9DKdnA9JRY
@yaito3014
yaito3014 merged commit ff61f6e into expected Jul 16, 2026
@yaito3014
yaito3014 deleted the claude/review-43-oubmru branch July 16, 2026 16:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants